[Checkins] SVN: zope.app.cache/trunk/ Use the RAM cache implementation from zope.ramcache.
Hanno Schlichting
hannosch at hannosch.eu
Thu Jul 23 06:09:01 EDT 2009
Log message for revision 102133:
Use the RAM cache implementation from zope.ramcache.
Changed:
U zope.app.cache/trunk/CHANGES.txt
U zope.app.cache/trunk/setup.py
U zope.app.cache/trunk/src/zope/app/cache/browser/configure.zcml
U zope.app.cache/trunk/src/zope/app/cache/browser/ram.py
U zope.app.cache/trunk/src/zope/app/cache/configure.zcml
U zope.app.cache/trunk/src/zope/app/cache/interfaces/__init__.py
U zope.app.cache/trunk/src/zope/app/cache/interfaces/ram.py
U zope.app.cache/trunk/src/zope/app/cache/ram.py
D zope.app.cache/trunk/src/zope/app/cache/tests/test_icache.py
D zope.app.cache/trunk/src/zope/app/cache/tests/test_ramcache.py
-=-
Modified: zope.app.cache/trunk/CHANGES.txt
===================================================================
--- zope.app.cache/trunk/CHANGES.txt 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/CHANGES.txt 2009-07-23 10:09:00 UTC (rev 102133)
@@ -2,9 +2,10 @@
CHANGES
=======
-3.6.1 (unreleased)
+3.7.0 (unreleased)
------------------
+- Use the RAM cache implementation from zope.ramcache.
3.6.0 (2009-05-27)
------------------
Modified: zope.app.cache/trunk/setup.py
===================================================================
--- zope.app.cache/trunk/setup.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/setup.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -22,7 +22,7 @@
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(name = 'zope.app.cache',
- version = '3.6.1dev',
+ version = '3.7.0dev',
author='Zope Corporation and Contributors',
author_email='zope-dev at zope.org',
description='Zope Caching Framework',
@@ -56,10 +56,10 @@
'zope.app.pagetemplate',
'zope.component',
'zope.componentvocabulary',
- 'zope.container',
'zope.interface',
'zope.proxy',
'zope.publisher',
+ 'zope.ramcache',
'zope.schema',
'zope.traversing',
],
Modified: zope.app.cache/trunk/src/zope/app/cache/browser/configure.zcml
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/browser/configure.zcml 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/browser/configure.zcml 2009-07-23 10:09:00 UTC (rev 102133)
@@ -6,12 +6,12 @@
<addMenuItem
title="RAM Cache"
description="A RAM cache is a volatile (in memory) cache"
- class="zope.app.cache.ram.RAMCache"
+ class="zope.ramcache.ram.RAMCache"
permission="zope.ManageServices"
/>
<pages
- for="zope.app.cache.interfaces.ram.IRAMCache"
+ for="zope.ramcache.interfaces.ram.IRAMCache"
class="zope.app.cache.browser.ram.RAMCacheView"
permission="zope.ManageServices">
<page name="editAction.html" attribute="action" />
Modified: zope.app.cache/trunk/src/zope/app/cache/browser/ram.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/browser/ram.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/browser/ram.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -18,7 +18,7 @@
__docformat__ = 'restructuredtext'
from zope.publisher.browser import BrowserView
-from zope.app.cache.interfaces.ram import IRAMCache
+from zope.ramcache.interfaces.ram import IRAMCache
class RAMCacheView(BrowserView):
Modified: zope.app.cache/trunk/src/zope/app/cache/configure.zcml
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/configure.zcml 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/configure.zcml 2009-07-23 10:09:00 UTC (rev 102133)
@@ -9,7 +9,7 @@
factory="zope.app.cache.annotationcacheable.AnnotationCacheable"
/>
- <class class=".ram.RAMCache">
+ <class class="zope.ramcache.ram.RAMCache">
<factory
id="zope.caching.RAMCache"
@@ -17,12 +17,12 @@
<implements
interface="zope.annotation.interfaces.IAttributeAnnotatable
- zope.app.cache.interfaces.ICache"
+ zope.ramcache.interfaces.ICache"
/>
- <require
+ <require
permission="zope.ManageServices"
- interface="zope.app.cache.interfaces.ram.IRAMCache"
+ interface="zope.ramcache.interfaces.ram.IRAMCache"
/>
</class>
Modified: zope.app.cache/trunk/src/zope/app/cache/interfaces/__init__.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/interfaces/__init__.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/interfaces/__init__.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -36,27 +36,5 @@
"""Sets the associated cache manager ID."""
-class ICache(Interface):
- """Interface for caches."""
-
- def invalidate(ob, key=None):
- """Invalidates cached entries that apply to the given object.
-
- `ob` is an object location. If `key` is specified, only
- invalidates entry for the given key. Otherwise invalidates
- all entries for the object.
- """
-
- def invalidateAll():
- """Invalidates all cached entries."""
-
- def query(ob, key=None, default=None):
- """Returns the cached data previously stored by `set()`.
-
- `ob` is the location of the content object being cached. `key` is
- a mapping of keywords and values which should all be used to
- select a cache entry.
- """
-
- def set(data, ob, key=None):
- """Stores the result of executing an operation."""
+# BBB import. Leave in place
+from zope.ramcache.interfaces import ICache
Modified: zope.app.cache/trunk/src/zope/app/cache/interfaces/ram.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/interfaces/ram.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/interfaces/ram.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -17,28 +17,5 @@
"""
__docformat__ = 'restructuredtext'
-from zope.interface import Attribute
-
-from zope.app.cache.interfaces import ICache
-
-class IRAMCache(ICache):
- """Interface for the RAM Cache."""
-
- maxEntries = Attribute("""A maximum number of cached values.""")
-
- maxAge = Attribute("""Maximum age for cached values in seconds.""")
-
- cleanupInterval = Attribute("""An interval between cache cleanups
- in seconds.""")
-
- def getStatistics():
- """Reports on the contents of a cache.
-
- The returned value is a sequence of dictionaries with the
- following keys:
-
- `path`, `hits`, `misses`, `size`, `entries`
- """
-
- def update(maxEntries, maxAge, cleanupInterval):
- """Saves the parameters available to the user"""
+# BBB import. Leave in place
+from zope.ramcache.interfaces.ram import IRAMCache
Modified: zope.app.cache/trunk/src/zope/app/cache/ram.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/ram.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/ram.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -17,331 +17,9 @@
"""
__docformat__ = 'restructuredtext'
-from time import time
-from threading import Lock
-from cPickle import dumps
-from persistent import Persistent
-from zope.interface import implements
-from zope.container.contained import Contained
-from zope.app.cache.interfaces.ram import IRAMCache
+# BBB imports. Leave in place
+from zope.ramcache.ram import RAMCache
+from zope.ramcache.ram import Storage
-
-# A global caches dictionary shared between threads
-caches = {}
-
-# A writelock for caches dictionary
-writelock = Lock()
-
-# A counter for cache ids and its lock
-cache_id_counter = 0
-cache_id_writelock = Lock()
-
-class RAMCache(Persistent, Contained):
- """RAM Cache
-
- The design of this class is heavily based on RAMCacheManager in
- Zope2.
-
- The idea behind the `RAMCache` is that it should be shared between
- threads, so that the same objects are not cached in each thread.
- This is achieved by storing the cache data structure itself as a
- module level variable (`RAMCache.caches`). This, of course,
- requires locking on modifications of that data structure.
-
- `RAMCache` is a persistent object. The actual data storage is a
- volatile object, which can be acquired/created by calling
- ``_getStorage()``. Storage objects are shared between threads and
- handle their blocking internally.
- """
-
- implements(IRAMCache)
-
- def __init__(self):
-
- # A timestamp and a counter are used here because using just a
- # timestamp and an id (address) produced unit test failures on
- # Windows (where ticks are 55ms long). If we want to use just
- # the counter, we need to make it persistent, because the
- # RAMCaches are persistent.
-
- cache_id_writelock.acquire()
- try:
- global cache_id_counter
- cache_id_counter +=1
- self._cacheId = "%s_%f_%d" % (id(self), time(), cache_id_counter)
- finally:
- cache_id_writelock.release()
-
- self.requestVars = ()
- self.maxEntries = 1000
- self.maxAge = 3600
- self.cleanupInterval = 300
-
- def getStatistics(self):
- s = self._getStorage()
- return s.getStatistics()
-
- def update(self, maxEntries=None, maxAge=None, cleanupInterval=None):
- if maxEntries is not None:
- self.maxEntries = maxEntries
-
- if maxAge is not None:
- self.maxAge = maxAge
-
- if cleanupInterval is not None:
- self.cleanupInterval = cleanupInterval
-
- self._getStorage().update(maxEntries, maxAge, cleanupInterval)
-
- def invalidate(self, ob, key=None):
- s = self._getStorage()
- if key:
- key = self._buildKey(key)
- s.invalidate(ob, key)
- else:
- s.invalidate(ob)
-
- def invalidateAll(self):
- s = self._getStorage()
- s.invalidateAll()
-
- def query(self, ob, key=None, default=None):
- s = self._getStorage()
- key = self._buildKey(key)
- try:
- return s.getEntry(ob, key)
- except KeyError:
- return default
-
- def set(self, data, ob, key=None):
- s = self._getStorage()
- key = self._buildKey(key)
- s.setEntry(ob, key, data)
-
- def _getStorage(self):
- "Finds or creates a storage object."
- cacheId = self._cacheId
- writelock.acquire()
- try:
- if cacheId not in caches:
- caches[cacheId] = Storage(self.maxEntries, self.maxAge,
- self.cleanupInterval)
- return caches[cacheId]
- finally:
- writelock.release()
-
- def _buildKey(kw):
- "Build a tuple which can be used as an index for a cached value"
- if kw:
- items = kw.items()
- items.sort()
- return tuple(items)
- return ()
-
- _buildKey = staticmethod(_buildKey)
-
-
-class Storage(object):
- """Storage.
-
- Storage keeps the count and does the aging and cleanup of cached
- entries.
-
- This object is shared between threads. It corresponds to a single
- persistent `RAMCache` object. Storage does the locking necessary
- for thread safety.
-
- """
-
- def __init__(self, maxEntries=1000, maxAge=3600, cleanupInterval=300):
- self._data = {}
- self._misses = {}
- self._invalidate_queue = []
- self.maxEntries = maxEntries
- self.maxAge = maxAge
- self.cleanupInterval = cleanupInterval
- self.writelock = Lock()
- self.lastCleanup = time()
-
- def update(self, maxEntries=None, maxAge=None, cleanupInterval=None):
- """Set the registration options.
-
- ``None`` values are ignored.
- """
- if maxEntries is not None:
- self.maxEntries = maxEntries
-
- if maxAge is not None:
- self.maxAge = maxAge
-
- if cleanupInterval is not None:
- self.cleanupInterval = cleanupInterval
-
- def getEntry(self, ob, key):
-
- if self.lastCleanup <= time() - self.cleanupInterval:
- self.cleanup()
-
- try:
- data = self._data[ob][key]
- except KeyError:
- if ob not in self._misses:
- self._misses[ob] = 0
- self._misses[ob] += 1
- raise
- else:
- data[2] += 1 # increment access count
- return data[0]
-
-
- def setEntry(self, ob, key, value):
- """Stores a value for the object. Creates the necessary
- dictionaries."""
-
- if self.lastCleanup <= time() - self.cleanupInterval:
- self.cleanup()
-
- self.writelock.acquire()
- try:
- if ob not in self._data:
- self._data[ob] = {}
-
- timestamp = time()
- # [data, ctime, access count]
- self._data[ob][key] = [value, timestamp, 0]
- finally:
- self.writelock.release()
- self._invalidate_queued()
-
- def _do_invalidate(self, ob, key=None):
- """This does the actual invalidation, but does not handle the locking.
-
- This method is supposed to be called from `invalidate`
- """
- try:
- if key is None:
- del self._data[ob]
- self._misses[ob] = 0
- else:
- del self._data[ob][key]
- if not self._data[ob]:
- del self._data[ob]
- except KeyError:
- pass
-
- def _invalidate_queued(self):
- """This method should be called after each writelock release."""
-
- while self._invalidate_queue:
- obj, key = self._invalidate_queue.pop()
- self.invalidate(obj, key)
-
- def invalidate(self, ob, key=None):
- """Drop the cached values.
-
- Drop all the values for an object if no key is provided or
- just one entry if the key is provided.
-
- """
- if self.writelock.acquire(0):
- try:
- self._do_invalidate(ob, key)
- finally:
- self.writelock.release()
- # self._invalidate_queued() not called to avoid a recursion
- else:
- self._invalidate_queue.append((ob, key))
-
- def invalidateAll(self):
- """Drop all the cached values.
- """
- self.writelock.acquire()
- try:
- self._data = {}
- self._misses = {}
- self._invalidate_queue = []
- finally:
- self.writelock.release()
-
- def removeStaleEntries(self):
- "Remove the entries older than `maxAge`"
-
- if self.maxAge > 0:
- punchline = time() - self.maxAge
- self.writelock.acquire()
- try:
- data = self._data
- for object, dict in data.items():
- for key in dict.keys():
- if dict[key][1] < punchline:
- del dict[key]
- if not dict:
- del data[object]
- finally:
- self.writelock.release()
- self._invalidate_queued()
-
- def cleanup(self):
- "Cleanup the data"
- self.removeStaleEntries()
- self.removeLeastAccessed()
- self.lastCleanup = time()
-
- def removeLeastAccessed(self):
- ""
-
- self.writelock.acquire()
- try:
- data = self._data
- keys = [(ob, k) for ob, v in data.iteritems() for k in v]
-
- if len(keys) > self.maxEntries:
- def getKey(item):
- ob, key = item
- return data[ob][key][2]
- keys.sort(key=getKey)
-
- ob, key = keys[self.maxEntries]
- maxDropCount = data[ob][key][2]
-
- keys.reverse()
-
- for ob, key in keys:
- if data[ob][key][2] <= maxDropCount:
- del data[ob][key]
- if not data[ob]:
- del data[ob]
-
- self._clearAccessCounters()
- finally:
- self.writelock.release()
- self._invalidate_queued()
-
- def _clearAccessCounters(self):
- for dict in self._data.itervalues():
- for val in dict.itervalues():
- val[2] = 0
- for k in self._misses:
- self._misses[k] = 0
-
- def getKeys(self, object):
- return self._data[object].keys()
-
- def getStatistics(self):
- "Basically see IRAMCache"
- objects = self._data.keys()
- objects.sort()
- result = []
-
- for ob in objects:
- size = len(dumps(self._data[ob]))
- hits = sum(entry[2] for entry in self._data[ob].itervalues())
- result.append({'path': ob,
- 'hits': hits,
- 'misses': self._misses.get(ob, 0),
- 'size': size,
- 'entries': len(self._data[ob])})
- return tuple(result)
-
__doc__ = RAMCache.__doc__ + __doc__
Deleted: zope.app.cache/trunk/src/zope/app/cache/tests/test_icache.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/tests/test_icache.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/tests/test_icache.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -1,75 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Unit tests for ICache interface
-
-$Id$
-"""
-from unittest import TestSuite, main
-from zope.interface.verify import verifyObject
-from zope.app.cache.interfaces import ICache
-
-
-class BaseICacheTest(object):
- """Base class for ICache unit tests. Subclasses should provide a
- _Test__new() method that returns a new empty cache object.
- """
-
- def testVerifyICache(self):
- # Verify that the object implements ICache
- verifyObject(ICache, self._Test__new())
-
- def testCaching(self):
- # Verify basic caching
- cache = self._Test__new()
- ob = "obj"
- data = "data"
- marker = []
- self.failIf(cache.query(ob, None, default=marker) is not marker,
- "empty cache should not contain anything")
-
- cache.set(data, ob, key={'id': 35})
- self.assertEquals(cache.query(ob, {'id': 35}), data,
- "should return cached result")
- self.failIf(cache.query(ob, {'id': 33}, default=marker) is not marker,
- "should not return cached result for a different key")
-
- cache.invalidate(ob, {"id": 33})
- self.assertEquals(cache.query(ob, {'id': 35}), data,
- "should return cached result")
- self.failIf(cache.query(ob, {'id': 33}, default=marker) is not marker,
- "should not return cached result after invalidate")
-
- def testInvalidateAll(self):
- cache = self._Test__new()
- ob1 = object()
- ob2 = object()
- cache.set("data1", ob1)
- cache.set("data2", ob2, key={'foo': 1})
- cache.set("data3", ob2, key={'foo': 2})
- cache.invalidateAll()
- marker = []
- self.failIf(cache.query(ob1, default=marker) is not marker,
- "should not return cached result after invalidateAll")
- self.failIf(cache.query(ob2, {'foo': 1}, default=marker) is not marker,
- "should not return cached result after invalidateAll")
- self.failIf(cache.query(ob2, {'foo': 2}, default=marker) is not marker,
- "should not return cached result after invalidateAll")
-
-
-def test_suite():
- return TestSuite((
- ))
-
-if __name__=='__main__':
- main(defaultTest='test_suite')
Deleted: zope.app.cache/trunk/src/zope/app/cache/tests/test_ramcache.py
===================================================================
--- zope.app.cache/trunk/src/zope/app/cache/tests/test_ramcache.py 2009-07-23 09:59:36 UTC (rev 102132)
+++ zope.app.cache/trunk/src/zope/app/cache/tests/test_ramcache.py 2009-07-23 10:09:00 UTC (rev 102133)
@@ -1,578 +0,0 @@
-#############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Unit tests for RAM Cache.
-
-$Id$
-"""
-from time import time
-from unittest import TestCase, TestSuite, main, makeSuite
-
-from zope.interface.verify import verifyClass, verifyObject
-from zope.interface import implements
-from zope.traversing.interfaces import IPhysicallyLocatable
-
-from zope.app.cache.ram import RAMCache
-from zope.app.cache.tests.test_icache import BaseICacheTest
-from zope.app.cache.interfaces import ICache
-from zope.app.cache.interfaces.ram import IRAMCache
-from zope.app.testing.placelesssetup import PlacelessSetup
-
-class Locatable(object):
-
- __name__ = __parent__ = None
-
- implements(IPhysicallyLocatable)
-
- def __init__(self, path=('a', 'b')):
- self.path = path
-
- def getRoot(self):
- return self
-
- def getPath(self):
- return self.path
-
-class TestRAMCache(PlacelessSetup,
- TestCase,
- BaseICacheTest,
- ):
-
- __name__ = __parent__ = None
-
- def _Test__new(self):
- from zope.app.cache.ram import RAMCache
- return RAMCache()
-
- def test_interface(self):
- verifyObject(IRAMCache, RAMCache())
- verifyClass(ICache, RAMCache)
-
- def test_init(self):
- from zope.app.cache.ram import RAMCache
- c1 = RAMCache()._cacheId
- c2 = RAMCache()._cacheId
- self.assertNotEquals(c1, c2,
- "The cacheId is not unique")
-
-
- def test_getStatistics(self):
- from zope.app.cache.ram import RAMCache
- c = RAMCache()
- c.set(42, "object", key={'foo': 'bar'})
- c.set(43, "object", key={'foo': 'bar'})
- c.query("object")
- c.query("object", key={'foo': 'bar'})
- r1 = c._getStorage().getStatistics()
- r2 = c.getStatistics()
-
- self.assertEqual(r1, r2, "see Storage.getStatistics() tests")
-
- def test_update(self):
- from zope.app.cache.ram import RAMCache
- c = RAMCache()
- c.update(1, 2, 3)
- s = c._getStorage()
- self.assertEqual(s.maxEntries, 1, "maxEntries not set")
- self.assertEqual(s.maxAge, 2, "maxAge not set")
- self.assertEqual(s.cleanupInterval, 3, "cleanupInterval not set")
-
- def test_timedCleanup(self):
- from zope.app.cache.ram import RAMCache
- import time
- c = RAMCache()
- c.update(cleanupInterval=1, maxAge=2)
- lastCleanup = c._getStorage().lastCleanup
- time.sleep(2)
- c.set(42, "object", key={'foo': 'bar'})
- # last cleanup should now be updated
- self.failUnless(lastCleanup < c._getStorage().lastCleanup)
-
- def test_cache(self):
- from zope.app.cache import ram
- self.assertEqual(type(ram.caches), type({}),
- 'no module level cache dictionary')
-
- def test_getStorage(self):
- from zope.app.cache.ram import RAMCache
- c = RAMCache()
- c.maxAge = 123
- c.maxEntries = 2002
- c.cleanupInterval = 42
- storage1 = c._getStorage()
- storage2 = c._getStorage()
- self.assertEqual(storage1, storage2,
- "_getStorage returns different storages")
-
- self.assertEqual(storage1.maxAge, 123,
- "maxAge not set (expected 123, got %s)"
- % storage1.maxAge)
- self.assertEqual(storage1.maxEntries, 2002,
- "maxEntries not set (expected 2002, got %s)"
- % storage1.maxEntries)
- self.assertEqual(storage1.cleanupInterval, 42,
- "cleanupInterval not set (expected 42, got %s)"
- % storage1.cleanupInterval)
-
- # Simulate persisting and restoring the RamCache which removes
- # all _v_ attributes.
- for k in c.__dict__.keys():
- if k.startswith('_v_'):
- del c.__dict__[k]
- storage2 = c._getStorage()
- self.assertEqual(storage1, storage2,
- "_getStorage returns different storages")
-
- def test_buildKey(self):
- from zope.app.cache.ram import RAMCache
-
- kw = {'foo': 1, 'bar': 2, 'baz': 3}
-
- key = RAMCache._buildKey(kw)
-
- self.assertEqual(key, (('bar',2), ('baz',3), ('foo',1)), "wrong key")
-
-
- def test_query(self):
- from zope.app.cache.ram import RAMCache
-
- ob = ('aaa',)
-
- keywords = {"answer": 42}
- value = "true"
- c = RAMCache()
- key = RAMCache._buildKey(keywords)
- c._getStorage().setEntry(ob, key, value)
-
- self.assertEqual(c.query(ob, keywords), value,
- "incorrect value")
-
- self.assertEqual(c.query(ob, None), None, "defaults incorrect")
- self.assertEqual(c.query(ob, {"answer": 2}, default="bummer"),
- "bummer", "default doesn't work")
-
- def test_set(self):
- from zope.app.cache.ram import RAMCache
-
- ob = ('path',)
- keywords = {"answer": 42}
- value = "true"
- c = RAMCache()
- c.requestVars = ('foo', 'bar')
- key = RAMCache._buildKey(keywords)
-
- c.set(value, ob, keywords)
- self.assertEqual(c._getStorage().getEntry(ob, key), value,
- "Not stored correctly")
-
-
- def test_invalidate(self):
- from zope.app.cache.ram import RAMCache
-
- ob1 = ("loc1",)
- ob2 = ("loc2",)
- keywords = {"answer": 42}
- keywords2 = {"answer": 41}
- value = "true"
- c = RAMCache()
- key1 = RAMCache._buildKey(keywords)
- key2 = RAMCache._buildKey(keywords)
- key3 = RAMCache._buildKey(keywords2)
-
- # Test invalidating entries with a keyword
- c._getStorage().setEntry(ob1, key1, value)
- c._getStorage().setEntry(ob2, key2, value)
- c._getStorage().setEntry(ob2, key3, value)
-
- c.invalidate(ob2, keywords)
-
- c._getStorage().getEntry(ob1, key1)
- self.assertRaises(KeyError, c._getStorage().getEntry, ob2, key2)
- c._getStorage().getEntry(ob2, key3)
-
- # Test deleting the whole object
- c._getStorage().setEntry(ob1, key1, value)
- c._getStorage().setEntry(ob2, key2, value)
- c._getStorage().setEntry(ob2, key3, value)
-
- c.invalidate(ob2)
- self.assertRaises(KeyError, c._getStorage().getEntry, ob2, key2)
- self.assertRaises(KeyError, c._getStorage().getEntry, ob2, key3)
- c._getStorage().getEntry(ob1, key1)
-
- # Try something that's not there
- c.invalidate(('yadda',))
-
-
-class TestStorage(TestCase):
-
- def test_getEntry(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- key = ('view', (), ('answer', 42))
- value = 'yes'
- timestamp = time()
-
- s._data = {object: {key: [value, timestamp, 1]}}
- self.assertEqual(s.getEntry(object, key), value, 'got wrong value')
-
- self.assert_(s._data[object][key][2] == 2, 'access count not updated')
-
- # See if _misses are updated
- try:
- s.getEntry(object, "Nonexistent")
- except KeyError:
- pass
- else:
- raise "ExpectedKeyError"
-
- self.assertEqual(s._misses[object], 1)
-
- object2 = "second"
- self.assert_(not s._misses.has_key(object2))
- try:
- s.getEntry(object2, "Nonexistent")
- except KeyError:
- pass
- else:
- raise "ExpectedKeyError"
- self.assertEqual(s._misses[object2], 1)
-
- def test_getEntry_do_cleanup(self):
- from zope.app.cache.ram import Storage
-
- s = Storage(cleanupInterval=300, maxAge=300)
- object = 'object'
- key = ('view', (), ('answer', 42))
- value = 'yes'
-
- s.setEntry(object, key, value)
-
- s._data[object][key][1] = time() - 400
- s.lastCleanup = time() - 400
-
- self.assertRaises(KeyError, s.getEntry, object, key)
-
- def test_setEntry(self):
- from zope.app.cache.ram import Storage
-
- s = Storage(cleanupInterval=300, maxAge=300)
- object = 'object'
- key = ('view', (), ('answer', 42))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
-
- t1 = time()
- s.setEntry(object, key, value)
- t2 = time()
-
- timestamp = s._data[object][key][1]
- self.failUnless(t1 <= timestamp <= t2, 'wrong timestamp')
-
- self.assertEqual(s._data, {object: {key: [value, timestamp, 0]}},
- 'stored data incorrectly')
-
- s._data[object][key][1] = time() - 400
- s.lastCleanup = time() - 400
-
- s.setEntry(object, key2, value)
-
- timestamp = s._data[object][key2][1]
- self.assertEqual(s._data, {object: {key2: [value, timestamp, 0]}},
- 'cleanup not called')
-
-
- def test_set_get(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- key = ('view', (), ('answer', 42))
- value = 'yes'
- s.setEntry(object, key, value)
- self.assertEqual(s.getEntry(object, key), value,
- 'got something other than set')
-
- def test_do_invalidate(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- object2 = 'object2'
- key = ('view', (), ('answer', 41))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
- ts = time()
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
- s._misses[object] = 42
- s._do_invalidate(object)
- self.assertEqual(s._data, {object2: {key: [value, ts, 0]}},
- 'invalidation failed')
- self.assertEqual(s._misses[object], 0, "misses counter not cleared")
-
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
- s._do_invalidate(object, key2)
- self.assertEqual(s._data,
- {object: {key: [value, ts, 0]},
- object2: {key: [value, ts, 0]}},
- 'invalidation of one key failed')
-
- def test_invalidate(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- object2 = 'object2'
- key = ('view', (), ('answer', 41))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
- ts = time()
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
-
- s.writelock.acquire()
- try:
- s.invalidate(object)
- finally:
- s.writelock.release()
- self.assertEqual(s._invalidate_queue, [(object, None)],
- "nothing in the invalidation queue")
-
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
- s.invalidate(object)
- self.assertEqual(s._data, {object2: {key: [value, ts, 0]}},
- "not invalidated")
-
- def test_invalidate_queued(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- object2 = 'object2'
- object3 = 'object3'
- key = ('view', (), ('answer', 41))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
- ts = time()
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]},
- object3: "foo" }
- s._invalidate_queue = [(object2, None), (object3, None)]
- s._invalidate_queued()
- self.assertEqual(s._data,
- {object: {key: [value, ts, 0], key2: [value, ts, 0]}},
- "failed to invalidate queued")
-
- def test_invalidateAll(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- object2 = 'object2'
- key = ('view', (), ('answer', 41))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
- ts = time()
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
- s._invalidate_queue = [(object, None)]
- s._misses = {object: 10, object2: 100}
- s.invalidateAll()
- self.assertEqual(s._data, {}, "not invalidated")
- self.assertEqual(s._misses, {}, "miss counters not reset")
- self.assertEqual(s._invalidate_queue, [], "invalidate queue not empty")
-
-
- def test_getKeys(self):
- from zope.app.cache.ram import Storage
- s = Storage()
- object = 'object'
- object2 = 'object2'
- key = ('view', (), ('answer', 41))
- key2 = ('view2', (), ('answer', 42))
- value = 'yes'
- ts = time()
- s._data = {object: {key: [value, ts, 0],
- key2: [value, ts, 0]},
- object2: {key: [value, ts, 0]}}
- keys = s.getKeys(object)
- expected = [key, key2]
- keys.sort()
- expected.sort()
- self.assertEqual(keys, expected, 'bad keys')
-
-
- def test_removeStale(self):
- from zope.app.cache.ram import Storage
- s = Storage(maxAge=100)
- object = 'object'
- object2 = 'object2'
- key = ('view', (), ('answer', 42))
- value = 'yes'
- timestamp = time()
- s._data = {object: {key: [value, timestamp-101, 2]},
- object2: {key: [value, timestamp-90, 0]}}
- s.removeStaleEntries()
- self.assertEqual(s._data, {object2: {key: [value, timestamp-90, 0]}},
- 'stale records removed incorrectly')
-
-
- s = Storage(maxAge=0)
- s._data = {object: {key: [value, timestamp, 2]},
- object2: {key: [value, timestamp-90, 0]}}
- d = s._data.copy()
- s.removeStaleEntries()
- self.assertEqual(s._data, d,
- 'records removed when maxAge == 0')
-
- def test_locking(self):
- from zope.app.cache.ram import Storage
- s = Storage(maxAge=100)
- s.writelock.acquire()
- try:
- self.assert_(s.writelock.locked(), "locks don't work")
- finally:
- s.writelock.release()
-
- def test_removeLeastAccessed(self):
- from zope.app.cache.ram import Storage
- s = Storage(maxEntries=3)
- object = 'object'
- object2 = 'object2'
- key1 = ('view1', (), ('answer', 42))
- key2 = ('view2', (), ('answer', 42))
- key3 = ('view3', (), ('answer', 42))
- value = 'yes'
- timestamp = time()
- s._data = {object: {key1: [value, 1, 10],
- key2: [value, 6, 5],
- key3: [value, 2, 2]},
- object2: {key1: [value, 5, 2],
- key2: [value, 3, 1],
- key3: [value, 4, 1]}}
- s.removeLeastAccessed()
- self.assertEqual(s._data,
- {object: {key1: [value, 1, 0],
- key2: [value, 6, 0]}},
- 'least records removed incorrectly')
-
- s = Storage(maxEntries=6)
- s._data = {object: {key1: [value, timestamp, 10],
- key2: [value, timestamp, 5],
- key3: [value, timestamp, 2]},
- object2: {key1: [value, timestamp, 2],
- key2: [value, timestamp, 1],
- key3: [value, timestamp, 1]}}
- c = s._data.copy()
- s.removeLeastAccessed()
- self.assertEqual(s._data, c, "modified list even though len < max")
-
- def test__clearAccessCounters(self):
- from zope.app.cache.ram import Storage
- s = Storage(maxEntries=3)
- object = 'object'
- object2 = 'object2'
- key1 = ('view1', (), ('answer', 42))
- key2 = ('view2', (), ('answer', 42))
- key3 = ('view3', (), ('answer', 42))
- value = 'yes'
- timestamp = time()
- s._data = {object: {key1: [value, 1, 10],
- key2: [value, 2, 5],
- key3: [value, 3, 2]},
- object2: {key1: [value, 4, 2],
- key2: [value, 5, 1],
- key3: [value, 6, 1]}}
- s._misses = {object: 4, object2: 2}
-
- cleared = {object: {key1: [value, 1, 0],
- key2: [value, 2, 0],
- key3: [value, 3, 0]},
- object2: {key1: [value, 4, 0],
- key2: [value, 5, 0],
- key3: [value, 6, 0]}}
- clearMisses = {object: 0, object2: 0}
-
- s._clearAccessCounters()
- self.assertEqual(s._data, cleared, "access counters not cleared")
- self.assertEqual(s._misses, clearMisses, "misses counter not cleared")
-
- def test_getStatistics(self):
- from zope.app.cache.ram import Storage, dumps
-
- s = Storage(maxEntries=3)
- object = 'object'
- object2 = 'object2'
- key1 = ('view1', (), ('answer', 42))
- key2 = ('view2', (), ('answer', 42))
- key3 = ('view3', (), ('answer', 42))
- value = 'yes'
- timestamp = time()
- s._data = {object: {key1: [value, 1, 10],
- key2: [value, 2, 5],
- key3: [value, 3, 2]},
- object2: {key1: [value, 4, 2],
- key2: [value, 5, 1],
- key3: [value, 6, 1]}}
- s._misses = {object: 11, object2: 42}
- len1 = len(dumps(s._data[object]))
- len2 = len(dumps(s._data[object2]))
-
- expected = ({'path': object,
- 'hits': 17,
- 'misses': 11,
- 'size': len1,
- 'entries': 3
- },
- {'path': object2,
- 'hits': 4,
- 'misses': 42,
- 'size': len2,
- 'entries': 3
- },
- )
-
- result = s.getStatistics()
-
- self.assertEqual(result, expected)
-
-
-class TestModule(TestCase):
-
- def test_locking(self):
- from zope.app.cache.ram import writelock
- writelock.acquire()
- try:
- self.failUnless(writelock.locked(), "locks don't work")
- finally:
- writelock.release()
-
-#############################################################################
-
-class Test(TestCase):
- pass
-
-def test_suite():
- return TestSuite((
- makeSuite(TestRAMCache),
- makeSuite(TestStorage),
- makeSuite(TestModule),
- ))
-
-if __name__=='__main__':
- main(defaultTest='test_suite')
More information about the Checkins
mailing list