Now that we're closer to switching to P3 for good, these helpers have outlived their usefulness, and mostly add noise. All remaining dict.iter*() or dict.view*() must be converted to the normal keys(), values() or items() calls. Whenever the result is likely to be used for more than the scope of a loop, or when the dict needs to be modified during iteration, the calls must be wrapped in a ``list()``, to protect the new P3 semantics. Those cases are very exceptional. Also removed some dead code or improved the API to remove unnecessary conversions.
129 lines
3.0 KiB
Python
129 lines
3.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
# taken from http://code.activestate.com/recipes/252524-length-limited-o1-lru-cache-implementation/
|
|
import threading
|
|
|
|
from .func import synchronized
|
|
|
|
__all__ = ['LRU']
|
|
|
|
class LRUNode(object):
|
|
__slots__ = ['prev', 'next', 'me']
|
|
def __init__(self, prev, me):
|
|
self.prev = prev
|
|
self.me = me
|
|
self.next = None
|
|
|
|
class LRU(object):
|
|
"""
|
|
Implementation of a length-limited O(1) LRU queue.
|
|
Built for and used by PyPE:
|
|
http://pype.sourceforge.net
|
|
Copyright 2003 Josiah Carlson.
|
|
"""
|
|
def __init__(self, count, pairs=[]):
|
|
self._lock = threading.RLock()
|
|
self.count = max(count, 1)
|
|
self.d = {}
|
|
self.first = None
|
|
self.last = None
|
|
for key, value in pairs:
|
|
self[key] = value
|
|
|
|
@synchronized()
|
|
def __contains__(self, obj):
|
|
return obj in self.d
|
|
|
|
def get(self, obj, val=None):
|
|
try:
|
|
return self[obj]
|
|
except KeyError:
|
|
return val
|
|
|
|
@synchronized()
|
|
def __getitem__(self, obj):
|
|
a = self.d[obj].me
|
|
self[a[0]] = a[1]
|
|
return a[1]
|
|
|
|
@synchronized()
|
|
def __setitem__(self, obj, val):
|
|
if obj in self.d:
|
|
del self[obj]
|
|
nobj = LRUNode(self.last, (obj, val))
|
|
if self.first is None:
|
|
self.first = nobj
|
|
if self.last:
|
|
self.last.next = nobj
|
|
self.last = nobj
|
|
self.d[obj] = nobj
|
|
if len(self.d) > self.count:
|
|
if self.first == self.last:
|
|
self.first = None
|
|
self.last = None
|
|
return
|
|
a = self.first
|
|
a.next.prev = None
|
|
self.first = a.next
|
|
a.next = None
|
|
del self.d[a.me[0]]
|
|
del a
|
|
|
|
@synchronized()
|
|
def __delitem__(self, obj):
|
|
nobj = self.d[obj]
|
|
if nobj.prev:
|
|
nobj.prev.next = nobj.next
|
|
else:
|
|
self.first = nobj.next
|
|
if nobj.next:
|
|
nobj.next.prev = nobj.prev
|
|
else:
|
|
self.last = nobj.prev
|
|
del self.d[obj]
|
|
|
|
@synchronized()
|
|
def __iter__(self):
|
|
cur = self.first
|
|
while cur is not None:
|
|
cur2 = cur.next
|
|
yield cur.me[1]
|
|
cur = cur2
|
|
|
|
@synchronized()
|
|
def __len__(self):
|
|
return len(self.d)
|
|
|
|
# FIXME: should this have a P2 and a P3 version or something?
|
|
@synchronized()
|
|
def iteritems(self):
|
|
cur = self.first
|
|
while cur is not None:
|
|
cur2 = cur.next
|
|
yield cur.me
|
|
cur = cur2
|
|
items = iteritems
|
|
|
|
@synchronized()
|
|
def iterkeys(self):
|
|
return iter(self.d)
|
|
|
|
@synchronized()
|
|
def itervalues(self):
|
|
return iter(self.d.values())
|
|
|
|
@synchronized()
|
|
def keys(self):
|
|
return list(self.d)
|
|
|
|
@synchronized()
|
|
def pop(self,key):
|
|
v=self[key]
|
|
del self[key]
|
|
return v
|
|
|
|
@synchronized()
|
|
def clear(self):
|
|
self.d = {}
|
|
self.first = None
|
|
self.last = None
|