Skip to content

Commit

Permalink
Fix #3: Improve documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
tkem committed Dec 19, 2014
1 parent 5c2a69b commit c6137f2
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 119 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Expand Up @@ -3,6 +3,8 @@

- Provide ``RRCache.choice`` property.

- Improve documentation.


0.8.2 2014-12-15
----------------
Expand Down
26 changes: 4 additions & 22 deletions cachetools/cache.py
Expand Up @@ -6,20 +6,7 @@ def one(value):


class Cache(collections.MutableMapping):
"""Mutable mapping to serve as a simple cache or cache base class.
This class discards arbitrary items using :meth:`popitem` to make
space when necessary. Derived classes may override
:meth:`popitem` to implement specific caching strategies. If a
subclass has to keep track of item access, insertion or deletion,
it may additionally need to override :meth:`__getitem__`,
:meth:`__setitem__` and :meth:`__delitem__`. If a subclass has to
keep meta data with its values, i.e. the `value` argument passed
to :meth:`Cache.__setitem__` is different from what a user would
regard as the cache's value, it will probably want to override
:meth:`getsizeof`, too.
"""
"""Mutable mapping to serve as a simple cache or cache base class."""

def __init__(self, maxsize, missing=None, getsizeof=None):
self.__data = dict()
Expand Down Expand Up @@ -82,21 +69,16 @@ def __len__(self):

@property
def maxsize(self):
"""Return the maximum size of the cache."""
"""The maximum size of the cache."""
return self.__maxsize

@property
def currsize(self):
"""Return the current size of the cache."""
"""The current size of the cache."""
return self.__currsize

def getsize(self, key):
import warnings
warnings.warn("Cache.getsize is deprecated", DeprecationWarning)
return self.__data[key][1]

def getsizeof(self, value):
"""Return the size of a cache element."""
"""Return the size of a cache element's value."""
return self.__getsizeof(value)

# collections.MutableMapping mixin methods do not handle __missing__
Expand Down
13 changes: 4 additions & 9 deletions cachetools/lfu.py
@@ -1,18 +1,13 @@
import collections
import operator

from .cache import Cache
from .decorators import cachedfunc
from .lock import RLock

import collections
import operator


class LFUCache(Cache):
"""Least Frequently Used (LFU) cache implementation.
This class counts how often an item is retrieved, and discards the
items used least often to make space when necessary.
"""
"""Least Frequently Used (LFU) cache implementation."""

def __init__(self, maxsize, missing=None, getsizeof=None):
Cache.__init__(self, maxsize, missing, getsizeof)
Expand Down
7 changes: 1 addition & 6 deletions cachetools/lru.py
Expand Up @@ -15,12 +15,7 @@ def unlink(self):


class LRUCache(Cache):
"""Least Recently Used (LRU) cache implementation.
This class discards the least recently used items first to make
space when necessary.
"""
"""Least Recently Used (LRU) cache implementation."""

def __init__(self, maxsize, missing=None, getsizeof=None):
if getsizeof is not None:
Expand Down
18 changes: 4 additions & 14 deletions cachetools/rr.py
@@ -1,22 +1,12 @@
import random

from .cache import Cache
from .decorators import cachedfunc
from .lock import RLock

import random


class RRCache(Cache):
"""Random Replacement (RR) cache implementation.
This class randomly selects candidate items and discards them to
make space when necessary.
By default, items are selected from the list of cache keys using
:func:`random.choice`. The optional argument `choice` may specify
an alternative function that returns an arbitrary element from a
non-empty sequence.
"""
"""Random Replacement (RR) cache implementation."""

def __init__(self, maxsize, choice=random.choice, missing=None,
getsizeof=None):
Expand All @@ -33,7 +23,7 @@ def popitem(self):

@property
def choice(self):
"""Return the `choice` function used by the cache."""
"""The `choice` function used by the cache."""
return self.__choice


Expand Down
38 changes: 11 additions & 27 deletions cachetools/ttl.py
@@ -1,10 +1,10 @@
import functools
import time

from .cache import Cache
from .decorators import cachedfunc
from .lock import RLock

import functools
import time


class Link(object):

Expand Down Expand Up @@ -56,21 +56,7 @@ def __getattr__(self, name):


class TTLCache(Cache):
"""LRU Cache implementation with per-item time-to-live (TTL) value.
This class associates a time-to-live value with each item. Items
that expire because they have exceeded their time-to-live will be
removed. If no expired items are there to remove, the least
recently used items will be discarded first to make space when
necessary. Trying to access an expired item will raise a
:exc:`KeyError`.
By default, the time-to-live is specified in seconds, and the
standard :func:`time.time` function is used to retrieve the
current time. A custom `timer` function can be supplied if
needed.
"""
"""LRU Cache implementation with per-item time-to-live (TTL) value."""

def __init__(self, maxsize, ttl, timer=time.time, missing=None,
getsizeof=None):
Expand Down Expand Up @@ -179,12 +165,7 @@ def __len__(self, cache_len=Cache.__len__):
return cache_len(self) - expired

def expire(self, time=None):
"""Remove expired items from the cache.
If `time` is not :const:`None`, remove all items whose
time-to-live would have expired by `time`.
"""
"""Remove expired items from the cache."""
if time is None:
time = self.__timer()
root = self.__root
Expand All @@ -197,7 +178,10 @@ def expire(self, time=None):
head = next

def popitem(self):
"""Remove and return the `(key, value)` pair least recently used."""
"""Remove and return the `(key, value)` pair least recently used that
has not already expired.
"""
with self.__timer as time:
self.expire(time)
root = self.__root
Expand All @@ -222,12 +206,12 @@ def currsize(self):

@property
def timer(self):
"""Return the timer used by the cache."""
"""The timer function used by the cache."""
return self.__timer

@property
def ttl(self):
"""Return the time-to-live of the cache."""
"""The time-to-live value of the cache's items."""
return self.__ttl

# mixin methods
Expand Down
94 changes: 70 additions & 24 deletions docs/index.rst
Expand Up @@ -26,10 +26,12 @@ function decorator.
For the purpose of this module, a *cache* is a mutable_ mapping_ of a
fixed maximum size. When the cache is full, i.e. by adding another
item the cache would exceed its maximum size, the cache must choose
which item(s) to discard based on a suitable `cache algorithm`_. A
cache's size is the sum of the size of its items, and an item's size
in general is a property or function of its value, e.g. the result of
:func:`sys.getsizeof`, or :func:`len` for string and sequence values.
which item(s) to discard based on a suitable `cache algorithm`_. In
general, a cache's size is the total size of its items, and an item's
size is a property or function of its value, e.g. the result of
``sys.getsizeof(value)``. For the trivial but common case that each
item counts as :const:`1`, irrespective of its value, a cache's size
is equal to the number of its items, or ``len(cache)``.

This module provides multiple cache implementations based on different
cache algorithms, as well as decorators for easily memoizing function
Expand All @@ -47,13 +49,13 @@ different cache algorithms. All these classes derive from class
current size of the cache.

All cache classes accept an optional `missing` keyword argument in
their constructor, which can be used to provide a default factory
function. If a key `key` is not present, the ``cache[key]`` operation
calls :meth:`Cache.__missing__`, which in turn calls `missing` with
`key` as argument. The cache will then store the object returned from
``missing(key)`` as the new cache value for `key`, possibly discarding
other items if the cache is full. This may be used to easily provide
caching for existing single-argument functions, for example::
their constructor, which can be used to provide a default *factory
function*. If the key `key` is not present, the ``cache[key]``
operation calls :meth:`Cache.__missing__`, which in turn calls
`missing` with `key` as its sole argument. The cache will then store
the object returned from ``missing(key)`` as the new cache value for
`key`, possibly discarding other items if the cache is full. This may
be used provide memoization for existing single-argument functions::

from cachetools import LRUCache
import urllib.request
Expand All @@ -75,32 +77,76 @@ caching for existing single-argument functions, for example::


:class:`Cache` also features a :meth:`getsizeof` method, which returns
the size of a given item. The default implementation of
:meth:`getsizeof` returns :const:`1` irrespective of its `value`
argument, making the cache's size equal to the number of its items, or
the size of a given `value`. The default implementation of
:meth:`getsizeof` returns :const:`1` irrespective of its argument,
making the cache's size equal to the number of its items, or
``len(cache)``. For convenience, all cache classes accept an optional
named constructor parameter `getsizeof`, which may specify a function
of one argument used to retrieve the size of an item's value.

.. autoclass:: Cache
:members:
:exclude-members: getsize

.. method:: getsize

.. deprecated:: 0.8.1
This class discards arbitrary items using :meth:`popitem` to make
space when necessary. Derived classes may override :meth:`popitem`
to implement specific caching strategies. If a subclass has to
keep track of item access, insertion or deletion, it may
additionally need to override :meth:`__getitem__`,
:meth:`__setitem__` and :meth:`__delitem__`. If a subclass wants
to store meta data with its values, i.e. the `value` argument
passed to :meth:`Cache.__setitem__` is different from what the
derived class's :meth:`__setitem__` received, it will probably need
to override :meth:`getsizeof`, too.

.. autoclass:: LFUCache
:members:

This class counts how often an item is retrieved, and discards the
items used least often to make space when necessary.

.. autoclass:: LRUCache
:members:

.. autoclass:: RRCache
This class discards the least recently used items first to make
space when necessary.

.. autoclass:: RRCache(maxsize, choice=random.choice, missing=None, getsizeof=None)
:members:

.. autoclass:: TTLCache
This class randomly selects candidate items and discards them to
make space when necessary.

By default, items are selected from the list of cache keys using
:func:`random.choice`. The optional argument `choice` may specify
an alternative function that returns an arbitrary element from a
non-empty sequence.

.. autoclass:: TTLCache(maxsize, ttl, timer=time.time, missing=None, getsizeof=None)
:members:
:exclude-members: expire

This class associates a time-to-live value with each item. Items
that expire because they have exceeded their time-to-live will be
removed automatically. If no expired items are there to remove,
the least recently used items will be discarded first to make space
when necessary. Trying to access an expired item will raise a
:exc:`KeyError`.

By default, the time-to-live is specified in seconds, and the
:func:`time.time` function is used to retrieve the current time. A
custom `timer` function can be supplied if needed.

.. automethod:: expire(self, time=None)

Since expired items will be "physically" removed from a cache
only at the next mutating operation, e.g. :meth:`__setitem__` or
:meth:`__delitem__`, to avoid changing the underlying dictionary
while iterating over it, expired items may still claim memory
although they are no longer accessible. Calling this method
removes all items whose time-to-live would have expired by
`time`, so garbage collection is free to reuse their memory. If
`time` is :const:`None`, this removes all items that have
expired by the current value returned by :attr:`timer`.


Function Decorators
Expand Down Expand Up @@ -147,10 +193,10 @@ The wrapped function is instrumented with :func:`cache_info` and
performance and clear the cache. See the :func:`functools.lru_cache`
documentation for details.

Like for :func:`functools.lru_cache`, the positional and keyword
arguments to the function must be hashable. Note that unlike
:func:`functools.lru_cache`, setting `maxsize` to :const:`None` is not
supported.
Like with :func:`functools.lru_cache`, the positional and keyword
arguments to the underlying function must be hashable. Note that
unlike :func:`functools.lru_cache`, setting `maxsize` to :const:`None`
is not supported.

.. decorator:: lfu_cache(maxsize=128, typed=False, getsizeof=None, lock=threading.RLock)

Expand Down
17 changes: 0 additions & 17 deletions tests/test_cache.py
Expand Up @@ -8,20 +8,3 @@ class CacheTest(unittest.TestCase, CacheTestMixin):

def cache(self, maxsize, missing=None, getsizeof=None):
return Cache(maxsize, missing=missing, getsizeof=getsizeof)

def test_getsize(self):
# Cache.getsize is deprecated
cache = self.cache(maxsize=3, getsizeof=lambda x: x)
cache.update({1: 1, 2: 2})

import warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
self.assertEqual(1, cache.getsize(1))
self.assertEqual(1, len(w))
self.assertEqual(w[0].category, DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
self.assertEqual(2, cache.getsize(2))
self.assertEqual(1, len(w))
self.assertEqual(w[0].category, DeprecationWarning)
3 changes: 3 additions & 0 deletions tests/test_ttl.py
Expand Up @@ -57,6 +57,7 @@ def test_lru(self):

def test_ttl(self):
cache = self.cache(maxsize=2, ttl=1)
self.assertEqual(0, cache.timer())
self.assertEqual(1, cache.ttl)

cache[1] = 1
Expand Down Expand Up @@ -116,6 +117,8 @@ def test_ttl(self):

def test_expire(self):
cache = self.cache(maxsize=3, ttl=2)
with cache.timer as time:
self.assertEqual(time, cache.timer())
self.assertEqual(2, cache.ttl)

cache[1] = 1
Expand Down

0 comments on commit c6137f2

Please sign in to comment.