mirror of https://github.com/mahmoud/boltons.git
add cacheutils.MinIDMap (though pypy support is incomplete)
This commit is contained in:
parent
ffbeaffb6b
commit
912af77d27
|
@ -34,6 +34,8 @@ Learn more about `caching algorithms on Wikipedia
|
||||||
# TODO: support 0 max_size?
|
# TODO: support 0 max_size?
|
||||||
|
|
||||||
|
|
||||||
|
import heapq
|
||||||
|
import weakref
|
||||||
import itertools
|
import itertools
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
@ -837,4 +839,57 @@ class ThresholdCounter(object):
|
||||||
if kwargs:
|
if kwargs:
|
||||||
self.update(kwargs)
|
self.update(kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MinIDMap(object):
|
||||||
|
"""
|
||||||
|
Assigns arbitrary weakref-able objects the smallest possible unique
|
||||||
|
integer IDs, such that no two objects have the same ID at the same
|
||||||
|
time.
|
||||||
|
|
||||||
|
Based on https://gist.github.com/kurtbrose/25b48114de216a5e55df
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.mapping = weakref.WeakKeyDictionary()
|
||||||
|
self.ref_map = {}
|
||||||
|
self.free = []
|
||||||
|
|
||||||
|
def get(self, a):
|
||||||
|
try:
|
||||||
|
return self.mapping[a][0] # if object is mapped, return ID
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.free: # if there are any free IDs, use the smallest
|
||||||
|
nxt = heapq.heappop(self.free)
|
||||||
|
else: # if there are no free numbers, use the next highest ID
|
||||||
|
nxt = len(self.mapping)
|
||||||
|
ref = weakref.ref(a, self._clean)
|
||||||
|
self.mapping[a] = (nxt, ref)
|
||||||
|
self.ref_map[ref] = nxt
|
||||||
|
return nxt
|
||||||
|
|
||||||
|
def drop(self, a):
|
||||||
|
freed, ref = self.mapping[a]
|
||||||
|
del self.mapping[a]
|
||||||
|
del self.ref_map[ref]
|
||||||
|
heapq.heappush(self.free, freed)
|
||||||
|
|
||||||
|
def _clean(self, ref):
|
||||||
|
print(self.ref_map[ref])
|
||||||
|
heapq.heappush(self.free, self.ref_map[ref])
|
||||||
|
del self.ref_map[ref]
|
||||||
|
|
||||||
|
def __contains__(self, a):
|
||||||
|
return a in self.mapping
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.mapping.itervalues()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.mapping.__len__()
|
||||||
|
|
||||||
|
def iteritems(self):
|
||||||
|
return self.mapping.iteritems()
|
||||||
|
|
||||||
|
|
||||||
# end cacheutils.py
|
# end cacheutils.py
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from boltons.cacheutils import LRU, LRI, cached, cachedmethod, cachedproperty
|
from boltons.cacheutils import LRU, LRI, cached, cachedmethod, cachedproperty, MinIDMap
|
||||||
|
|
||||||
|
|
||||||
class CountingCallable(object):
|
class CountingCallable(object):
|
||||||
|
@ -293,3 +293,24 @@ def test_cachedproperty():
|
||||||
assert prop.expensive_func.call_count == 2
|
assert prop.expensive_func.call_count == 2
|
||||||
|
|
||||||
repr(Proper.useful_attr)
|
repr(Proper.useful_attr)
|
||||||
|
|
||||||
|
|
||||||
|
def test_min_id_map():
|
||||||
|
import sys
|
||||||
|
if '__pypy__' in sys.builtin_module_names:
|
||||||
|
return # TODO: pypy still needs some work
|
||||||
|
|
||||||
|
midm = MinIDMap()
|
||||||
|
|
||||||
|
class Foo(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# use this circular array to have them periodically collected
|
||||||
|
ref_wheel = [None, None, None]
|
||||||
|
|
||||||
|
for i in range(1000):
|
||||||
|
nxt = Foo()
|
||||||
|
ref_wheel[i % len(ref_wheel)] = nxt
|
||||||
|
assert midm.get(nxt) <= len(ref_wheel)
|
||||||
|
if i % 10 == 0:
|
||||||
|
midm.drop(nxt)
|
||||||
|
|
Loading…
Reference in New Issue