[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