from __future__ import absolute_import
import collections
import functools
import s


# DO NOT use these to decorate class methods
# todo how to enforce this at decoration time?


_attr = '_cached_value'


def func(fn):
    def cached_fn(*a, **kw):
        cached_fn.clear_cache = lambda: delattr(fn, _attr)
        if not hasattr(fn, _attr):
            setattr(fn, _attr, fn(*a, **kw))
        return getattr(fn, _attr)
    cached_fn.clear_cache = lambda: None
    with s.exceptions.ignore(AttributeError):
        cached_fn = functools.wraps(callable)(cached_fn)
    return cached_fn


def memoize(max=1000):
    def decorator(fn):
        @functools.wraps(fn)
        def decorated(*a, **kw):
            key = tuple(map(s.data.immutalize, [a, kw.items()]))
            if key not in decorated._data:
                result = fn(*a, **kw)
            else:
                result = decorated._data.pop(key)
            decorated._data[key] = result
            while len(decorated._data) > max: # trim lru to max
                decorated._data.popitem(last=False)
            return result
        decorated._data = collections.OrderedDict()
        return decorated
    return decorator

@@ -582,16 +582,60 @@
 ed_fn%0A%0A%0A
+@s.hacks.optionally_parameterized_decorator%0A
 def memo
@@ -645,13 +645,19 @@
 (max
+_keys
 =1000
+0
 ):%0A 
@@ -732,32 +732,78 @@
 ated(*a, **kw):%0A
+            cache = getattr(decorated, _attr)%0A
             key 
@@ -877,31 +877,21 @@
  not in 
-decorated._data
+cache
 :%0A      
@@ -965,31 +965,21 @@
 esult = 
-decorated._data
+cache
 .pop(key
@@ -992,31 +992,21 @@
         
-decorated._data
+cache
 %5Bkey%5D = 
@@ -1038,30 +1038,25 @@
 len(
-decorated._data
+cache
 ) %3E max
+_keys
 : # 
@@ -1070,16 +1070,21 @@
 u to max
+_keys
 %0A       
@@ -1092,31 +1092,21 @@
         
-decorated._data
+cache
 .popitem
@@ -1152,16 +1152,24 @@
         
+setattr(
 decorate
@@ -1173,16 +1173,16 @@
 ated
-._data =
+, _attr,
  col
@@ -1203,16 +1203,17 @@
 edDict()
+)
 %0A       

make memoize optionally parameterized