web/lib/django/core/cache/backends/memcached.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/core/cache/backends/memcached.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/core/cache/backends/memcached.py	Tue May 25 02:43:45 2010 +0200
@@ -1,10 +1,17 @@
 "Memcached cache backend"
 
+import time
+
 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
 from django.utils.encoding import smart_unicode, smart_str
 
 try:
     import cmemcache as memcache
+    import warnings
+    warnings.warn(
+        "Support for the 'cmemcache' library has been deprecated. Please use python-memcached instead.",
+        PendingDeprecationWarning
+    )
 except ImportError:
     try:
         import memcache
@@ -16,25 +23,35 @@
         BaseCache.__init__(self, params)
         self._cache = memcache.Client(server.split(';'))
 
+    def _get_memcache_timeout(self, timeout):
+        """
+        Memcached deals with long (> 30 days) timeouts in a special
+        way. Call this function to obtain a safe value for your timeout.
+        """
+        timeout = timeout or self.default_timeout
+        if timeout > 2592000: # 60*60*24*30, 30 days
+            # See http://code.google.com/p/memcached/wiki/FAQ
+            # "You can set expire times up to 30 days in the future. After that
+            # memcached interprets it as a date, and will expire the item after
+            # said date. This is a simple (but obscure) mechanic."
+            #
+            # This means that we have to switch to absolute timestamps.
+            timeout += int(time.time())
+        return timeout
+
     def add(self, key, value, timeout=0):
         if isinstance(value, unicode):
             value = value.encode('utf-8')
-        return self._cache.add(smart_str(key), value, timeout or self.default_timeout)
+        return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout))
 
     def get(self, key, default=None):
         val = self._cache.get(smart_str(key))
         if val is None:
             return default
-        else:
-            if isinstance(val, basestring):
-                return smart_unicode(val)
-            else:
-                return val
+        return val
 
     def set(self, key, value, timeout=0):
-        if isinstance(value, unicode):
-            value = value.encode('utf-8')
-        self._cache.set(smart_str(key), value, timeout or self.default_timeout)
+        self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))
 
     def delete(self, key):
         self._cache.delete(smart_str(key))
@@ -46,7 +63,42 @@
         self._cache.disconnect_all()
 
     def incr(self, key, delta=1):
-        return self._cache.incr(key, delta)
+        try:
+            val = self._cache.incr(key, delta)
+
+        # python-memcache responds to incr on non-existent keys by
+        # raising a ValueError. Cmemcache returns None. In both
+        # cases, we should raise a ValueError though.
+        except ValueError:
+            val = None
+        if val is None:
+            raise ValueError("Key '%s' not found" % key)
+
+        return val
 
     def decr(self, key, delta=1):
-        return self._cache.decr(key, delta)
+        try:
+            val = self._cache.decr(key, delta)
+
+        # python-memcache responds to decr on non-existent keys by
+        # raising a ValueError. Cmemcache returns None. In both
+        # cases, we should raise a ValueError though.
+        except ValueError:
+            val = None
+        if val is None:
+            raise ValueError("Key '%s' not found" % key)
+        return val
+
+    def set_many(self, data, timeout=0):
+        safe_data = {}
+        for key, value in data.items():
+            if isinstance(value, unicode):
+                value = value.encode('utf-8')
+            safe_data[smart_str(key)] = value
+        self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout))
+
+    def delete_many(self, keys):
+        self._cache.delete_multi(map(smart_str, keys))
+
+    def clear(self):
+        self._cache.flush_all()