From 3c1094359d0080f67777b6db79442c7074f2a6ba Mon Sep 17 00:00:00 2001 From: Mahmoud Hashemi Date: Tue, 17 May 2016 01:32:55 -0700 Subject: [PATCH] initial implementation of cachedproperty and tests, addressing #66. docs soon. --- boltons/cacheutils.py | 16 ++++++++++++++++ tests/test_cacheutils.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/boltons/cacheutils.py b/boltons/cacheutils.py index 780530e..8df1470 100644 --- a/boltons/cacheutils.py +++ b/boltons/cacheutils.py @@ -572,6 +572,22 @@ def cachedmethod(cache, typed=False, selfish=True): return cached_method_decorator +class cachedproperty(object): + def __init__(self, func): + self.func = func + + def __get__(self, obj, objtype=None): + if obj is None: + return self + value = self.func(obj) + setattr(obj, self.func.__name__, value) + return value + + def __repr__(self): + cn = self.__class__.__name__ + return '<%s func=%s>' % (cn, self.func) + + class ThresholdCounter(object): """A **bounded** dict-like Mapping from keys to counts. The ThresholdCounter automatically compacts after every (1 / diff --git a/tests/test_cacheutils.py b/tests/test_cacheutils.py index 473069f..0312769 100644 --- a/tests/test_cacheutils.py +++ b/tests/test_cacheutils.py @@ -2,7 +2,7 @@ import string -from boltons.cacheutils import LRU, LRI, cached, cachedmethod +from boltons.cacheutils import LRU, LRI, cached, cachedmethod, cachedproperty class CountingCallable(object): @@ -242,3 +242,31 @@ def test_cachedmethod(): print(repr(car_two.door)) print(repr(Car.door)) return + + +def test_cachedproperty(): + class Proper(object): + def __init__(self): + self.expensive_func = CountingCallable() + + @cachedproperty + def useful_attr(self): + return self.expensive_func() + + prop = Proper() + + assert prop.expensive_func.call_count == 0 + assert prop.useful_attr == 1 + assert prop.expensive_func.call_count == 1 + assert prop.useful_attr == 1 + assert prop.expensive_func.call_count == 1 + + prop.useful_attr += 1 # would not be possible with normal properties + assert prop.useful_attr == 2 + + delattr(prop, 'useful_attr') + assert prop.expensive_func.call_count == 1 + assert prop.useful_attr + assert prop.expensive_func.call_count == 2 + + repr(Proper.useful_attr)