Skip to content
Snippets Groups Projects
Commit 8cd0f064 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Merge branch 'enh/lrucache' into 'master'

enh/lrucache

See merge request fsl/fslpy!152
parents 60a468c7 f95d75ac
No related branches found
No related tags found
No related merge requests found
......@@ -2,6 +2,18 @@ This document contains the ``fslpy`` release history in reverse chronological
order.
2.6.0 (Under development)
-------------------------
Changed
^^^^^^^
* The :class:`.Cache` class has a new ``lru`` option, allowing it to be used
as a least-recently-used cache.
2.5.0 (Tuesday 6th August 2019)
-------------------------------
......
......@@ -42,14 +42,20 @@ class Cache(object):
raised.
"""
def __init__(self, maxsize=100):
def __init__(self, maxsize=100, lru=False):
"""Create a ``Cache``.
:arg maxsize: Maximum number of items allowed in the ``Cache`` before
it starts dropping old items
:arg lru: (least recently used) If ``False`` (the default), items
are dropped according to their insertion time. Otherwise,
items are dropped according to their most recent access
time.
"""
self.__cache = collections.OrderedDict()
self.__maxsize = maxsize
self.__lru = lru
def put(self, key, value, expiry=0):
......@@ -94,14 +100,26 @@ class Cache(object):
else:
entry = self.__cache[key]
# Check to see if the entry
# has expired
now = time.time()
if entry.expiry > 0:
if time.time() - entry.storetime > entry.expiry:
if now - entry.storetime > entry.expiry:
self.__cache.pop(key)
if defaultSpecified: return default
else: raise Expired(key)
# If we are an lru cache, update
# this entry's expiry, and update
# its order in the cache dict
if self.__lru:
entry.storetime = now
self.__cache.pop(key)
self.__cache[key] = entry
return entry.value
......@@ -125,6 +143,13 @@ class Cache(object):
return self.put(key, value)
def __contains__(self, key):
"""Check whether an item is in the cache. Note that the item may
be in the cache, but it may be expired.
"""
return key in self.__cache
def __parseDefault(self, *args, **kwargs):
"""Used by the :meth:`get` method. Parses the ``default`` argument,
which may be specified as either a positional or keyword argumnet.
......@@ -134,7 +159,7 @@ class Cache(object):
- ``True`` if a default argument was specified, ``False``
otherwise.
- The specifeid default value, or ``None`` if it wasn't
- The specified default value, or ``None`` if it wasn't
specified.
"""
......
......@@ -113,7 +113,36 @@ def test_expiry():
with pytest.raises(cache.Expired):
c.get(0)
with pytest.raises(cache.Expired):
c.get(1)
assert c.get(1, default='default') == 'default'
# And that the cache is empty
assert len(c) == 0
def test_lru():
c = cache.Cache(maxsize=3, lru=True)
c[0] = '0'
c[1] = '1'
c[2] = '2'
c[3] = '3'
# normal behaviour - first inserted
# is dropped
with pytest.raises(KeyError):
assert c.get(0)
# lru behaviour - oldest accessed is
# dropped
c[1]
c[4] = '4'
with pytest.raises(KeyError):
c[2]
c[1]
c[3]
c[4]
assert len(c) == 3
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment