@@ -267,13 +267,14 @@ def make_typed_key(args: tuple, kwds: dict):
267267
268268def _cached_wrapper (
269269 func ,
270- cache : BaseCacheImpl ,
270+ cache : typing . Union [ BaseCacheImpl , typing . Callable ] ,
271271 key_maker : typing .Callable [[tuple , dict ], typing .Hashable ],
272272 clear_reuse : bool ,
273273 callback : typing .Optional [typing .Callable [[int , typing .Any , typing .Any ], typing .Any ]],
274274 copy_level : int ,
275275 is_method : bool ,
276276):
277+ is_method = cache_is_function = inspect .isfunction (cache )
277278 _key_maker = (lambda args , kwds : key_maker (args [1 :], kwds )) if is_method else key_maker
278279
279280 hits = 0
@@ -287,11 +288,12 @@ def _wrapped(*args, **kwds):
287288 if kwds .pop ("cachebox__ignore" , False ):
288289 return func (* args , ** kwds )
289290
291+ _cache = cache (args [0 ]) if cache_is_function else cache
290292 key = _key_maker (args , kwds )
291293
292294 # try to get result from cache
293295 try :
294- result = cache [key ]
296+ result = _cache [key ]
295297 except KeyError :
296298 pass
297299 else :
@@ -310,7 +312,7 @@ def _wrapped(*args, **kwds):
310312 raise cached_error
311313
312314 try :
313- result = cache [key ]
315+ result = _cache [key ]
314316 hits += 1
315317 event = EVENT_HIT
316318 except KeyError :
@@ -323,7 +325,7 @@ def _wrapped(*args, **kwds):
323325 raise e
324326
325327 else :
326- cache [key ] = result
328+ _cache [key ] = result
327329 misses += 1
328330 event = EVENT_MISS
329331
@@ -332,34 +334,39 @@ def _wrapped(*args, **kwds):
332334
333335 return _copy_if_need (result , level = copy_level )
334336
335- _wrapped .cache = cache
337+ if not cache_is_function :
338+ _wrapped .cache = cache
339+ _wrapped .cache_info = lambda : CacheInfo (
340+ hits , misses , cache .maxsize , len (cache ), cache .capacity ()
341+ )
342+
336343 _wrapped .callback = callback
337- _wrapped .cache_info = lambda : CacheInfo (
338- hits , misses , cache .maxsize , len (cache ), cache .capacity ()
339- )
340344
341- def cache_clear () -> None :
342- nonlocal misses , hits , locks , exceptions
343- cache .clear (reuse = clear_reuse )
344- misses = 0
345- hits = 0
346- locks .clear ()
347- exceptions .clear ()
345+ if not cache_is_function :
346+
347+ def cache_clear () -> None :
348+ nonlocal misses , hits , locks , exceptions
349+ cache .clear (reuse = clear_reuse )
350+ misses = 0
351+ hits = 0
352+ locks .clear ()
353+ exceptions .clear ()
348354
349- _wrapped .cache_clear = cache_clear
355+ _wrapped .cache_clear = cache_clear
350356
351357 return _wrapped
352358
353359
354360def _async_cached_wrapper (
355361 func ,
356- cache : BaseCacheImpl ,
362+ cache : typing . Union [ BaseCacheImpl , typing . Callable ] ,
357363 key_maker : typing .Callable [[tuple , dict ], typing .Hashable ],
358364 clear_reuse : bool ,
359365 callback : typing .Optional [typing .Callable [[int , typing .Any , typing .Any ], typing .Any ]],
360366 copy_level : int ,
361367 is_method : bool ,
362368):
369+ is_method = cache_is_function = inspect .isfunction (cache )
363370 _key_maker = (lambda args , kwds : key_maker (args [1 :], kwds )) if is_method else key_maker
364371
365372 hits = 0
@@ -375,11 +382,12 @@ async def _wrapped(*args, **kwds):
375382 if kwds .pop ("cachebox__ignore" , False ):
376383 return await func (* args , ** kwds )
377384
385+ _cache = cache (args [0 ]) if cache_is_function else cache
378386 key = _key_maker (args , kwds )
379387
380388 # try to get result from cache
381389 try :
382- result = cache [key ]
390+ result = _cache [key ]
383391 except KeyError :
384392 pass
385393 else :
@@ -400,7 +408,7 @@ async def _wrapped(*args, **kwds):
400408 raise cached_error
401409
402410 try :
403- result = cache [key ]
411+ result = _cache [key ]
404412 hits += 1
405413 event = EVENT_HIT
406414 except KeyError :
@@ -413,7 +421,7 @@ async def _wrapped(*args, **kwds):
413421 raise e
414422
415423 else :
416- cache [key ] = result
424+ _cache [key ] = result
417425 misses += 1
418426 event = EVENT_MISS
419427
@@ -424,21 +432,25 @@ async def _wrapped(*args, **kwds):
424432
425433 return _copy_if_need (result , level = copy_level )
426434
427- _wrapped .cache = cache
435+ if not cache_is_function :
436+ _wrapped .cache = cache
437+ _wrapped .cache_info = lambda : CacheInfo (
438+ hits , misses , cache .maxsize , len (cache ), cache .capacity ()
439+ )
440+
428441 _wrapped .callback = callback
429- _wrapped .cache_info = lambda : CacheInfo (
430- hits , misses , cache .maxsize , len (cache ), cache .capacity ()
431- )
432442
433- def cache_clear () -> None :
434- nonlocal misses , hits , locks , exceptions
435- cache .clear (reuse = clear_reuse )
436- misses = 0
437- hits = 0
438- locks .clear ()
439- exceptions .clear ()
443+ if not cache_is_function :
440444
441- _wrapped .cache_clear = cache_clear
445+ def cache_clear () -> None :
446+ nonlocal misses , hits , locks , exceptions
447+ cache .clear (reuse = clear_reuse )
448+ misses = 0
449+ hits = 0
450+ locks .clear ()
451+ exceptions .clear ()
452+
453+ _wrapped .cache_clear = cache_clear
442454
443455 return _wrapped
444456
@@ -456,7 +468,8 @@ def cached(
456468 Wraps a function to automatically cache and retrieve its results based on input parameters.
457469
458470 Args:
459- cache (BaseCacheImpl, dict, optional): Cache implementation to store results. Defaults to FIFOCache.
471+ cache (BaseCacheImpl, dict, callable): Cache implementation to store results. Defaults to FIFOCache.
472+ Can be a function that got `self` and should return cache.
460473 key_maker (Callable, optional): Function to generate cache keys from function arguments. Defaults to make_key.
461474 clear_reuse (bool, optional): Whether to reuse cache during clearing. Defaults to False.
462475 callback (Callable, optional): Function called on cache hit/miss events. Defaults to None.
@@ -465,7 +478,7 @@ def cached(
465478 Returns:
466479 Callable: Decorated function with caching capabilities.
467480
468- Example::
481+ Example for functions ::
469482
470483 @cachebox.cached(cachebox.LRUCache(128))
471484 def sum_as_string(a, b):
@@ -476,15 +489,29 @@ def sum_as_string(a, b):
476489 assert len(sum_as_string.cache) == 1
477490 sum_as_string.cache_clear()
478491 assert len(sum_as_string.cache) == 0
492+
493+ Example for methods::
494+
495+ class A:
496+ def __init__(self, num):
497+ self.num = num
498+ self._cache = cachebox.FIFOCache(0)
499+
500+ @cachebox.cached(lambda self: self._cache)
501+ def method(self, n):
502+ return self.num * n
503+
504+ instance = A(10)
505+ assert A.method(2) == 20
479506 """
480507 if cache is None :
481508 cache = FIFOCache (0 )
482509
483510 if type (cache ) is dict :
484511 cache = FIFOCache (0 , cache )
485512
486- if not isinstance (cache , BaseCacheImpl ):
487- raise TypeError ("we expected cachebox caches, got %r" % (cache ,))
513+ if not isinstance (cache , BaseCacheImpl ) and not inspect . isfunction ( cache ) :
514+ raise TypeError ("we expected cachebox caches or function , got %r" % (cache ,))
488515
489516 def decorator (func : FT ) -> FT :
490517 if inspect .iscoroutinefunction (func ):
@@ -509,6 +536,9 @@ def cachedmethod(
509536 copy_level : int = 1 ,
510537) -> typing .Callable [[FT ], FT ]:
511538 """
539+ **This function is deperecated due to issue [#35](https://github.com/awolverp/cachebox/issues/35)**.
540+ Use `cached` method instead.
541+
512542 Decorator to create a method-specific memoized cache for function results.
513543
514544 Similar to `cached()`, but ignores `self` parameter when generating cache keys.
@@ -523,6 +553,14 @@ def cachedmethod(
523553 Returns:
524554 Callable: Decorated method with method-specific caching capabilities.
525555 """
556+ import warnings
557+
558+ warnings .warn (
559+ "cachedmethod is deprecated, use cached instead. see issue https://github.com/awolverp/cachebox/issues/35" ,
560+ DeprecationWarning ,
561+ stacklevel = 2 ,
562+ )
563+
526564 if cache is None :
527565 cache = FIFOCache (0 )
528566
0 commit comments