simplified loader api and added builtin caching
--HG--
branch : trunk
diff --git a/jinja2/utils.py b/jinja2/utils.py
index 63394ce..c030d24 100644
--- a/jinja2/utils.py
+++ b/jinja2/utils.py
@@ -10,6 +10,8 @@
"""
import re
import string
+from collections import deque
+from copy import deepcopy
from functools import update_wrapper
from itertools import imap
@@ -196,3 +198,122 @@
__repr__ = lambda s: str(repr(escape(s.obj)))
__int__ = lambda s: int(s.obj)
__float__ = lambda s: float(s.obj)
+
+
+class LRUCache(object):
+ """A simple LRU Cache implementation."""
+ # this is fast for small capacities (something around 200) but doesn't
+ # scale. But as long as it's only used for the database connections in
+ # a non request fallback it's fine.
+
+ def __init__(self, capacity):
+ self.capacity = capacity
+ self._mapping = {}
+ self._queue = deque()
+
+ # alias all queue methods for faster lookup
+ self._popleft = self._queue.popleft
+ self._pop = self._queue.pop
+ if hasattr(self._queue, 'remove'):
+ self._remove = self._queue.remove
+ self._append = self._queue.append
+
+ def _remove(self, obj):
+ """Python 2.4 compatibility."""
+ for idx, item in enumerate(self._queue):
+ if item == obj:
+ del self._queue[idx]
+ break
+
+ def copy(self):
+ """Return an shallow copy of the instance."""
+ rv = LRUCache(self.capacity)
+ rv._mapping.update(self._mapping)
+ rv._queue = self._queue[:]
+ return rv
+
+ def get(self, key, default=None):
+ """Return an item from the cache dict or `default`"""
+ if key in self:
+ return self[key]
+ return default
+
+ def setdefault(self, key, default=None):
+ """
+ Set `default` if the key is not in the cache otherwise
+ leave unchanged. Return the value of this key.
+ """
+ if key in self:
+ return self[key]
+ self[key] = default
+ return default
+
+ def clear(self):
+ """Clear the cache."""
+ self._mapping.clear()
+ self._queue.clear()
+
+ def __contains__(self, key):
+ """Check if a key exists in this cache."""
+ return key in self._mapping
+
+ def __len__(self):
+ """Return the current size of the cache."""
+ return len(self._mapping)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self._mapping
+ )
+
+ def __getitem__(self, key):
+ """Get an item from the cache. Moves the item up so that it has the
+ highest priority then.
+
+ Raise an `KeyError` if it does not exist.
+ """
+ rv = self._mapping[key]
+ if self._queue[-1] != key:
+ self._remove(key)
+ self._append(key)
+ return rv
+
+ def __setitem__(self, key, value):
+ """Sets the value for an item. Moves the item up so that it
+ has the highest priority then.
+ """
+ if key in self._mapping:
+ self._remove(key)
+ elif len(self._mapping) == self.capacity:
+ del self._mapping[self._popleft()]
+ self._append(key)
+ self._mapping[key] = value
+
+ def __delitem__(self, key):
+ """Remove an item from the cache dict.
+ Raise an `KeyError` if it does not exist.
+ """
+ del self._mapping[key]
+ self._remove(key)
+
+ def __iter__(self):
+ """Iterate over all values in the cache dict, ordered by
+ the most recent usage.
+ """
+ return reversed(self._queue)
+
+ def __reversed__(self):
+ """Iterate over the values in the cache dict, oldest items
+ coming first.
+ """
+ return iter(self._queue)
+
+ __copy__ = copy
+
+ def __deepcopy__(self):
+ """Return a deep copy of the LRU Cache"""
+ rv = LRUCache(self.capacity)
+ rv._mapping = deepcopy(self._mapping)
+ rv._queue = deepcopy(self._queue)
+ return rv