1
0
mirror of https://github.com/django/django.git synced 2025-11-07 07:15:35 +00:00

Add warning when using cache keys that might not work with memcached.

This means testing with local dev caches (not memcache) will warn
developers if they are introducing inadvertent importabilities. There is
also the ability to silence the warning if a dev is not planning to use
memcache and knows what they are doing with their keys.

Thanks to Carl Meyer for the patch. Fixed #6447.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13766 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick
2010-09-12 18:45:26 +00:00
parent bfbc259de0
commit fc26da645a
10 changed files with 167 additions and 10 deletions

View File

@@ -18,7 +18,7 @@ See docs/cache.txt for information on the public API.
from cgi import parse_qsl
from django.conf import settings
from django.core import signals
from django.core.cache.backends.base import InvalidCacheBackendError
from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
from django.utils import importlib
# Name for use in settings file --> name of module in "backends" directory.

View File

@@ -1,10 +1,18 @@
"Base Cache class."
from django.core.exceptions import ImproperlyConfigured
import warnings
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
class InvalidCacheBackendError(ImproperlyConfigured):
pass
class CacheKeyWarning(DjangoRuntimeWarning):
pass
# Memcached does not accept keys longer than this.
MEMCACHE_MAX_KEY_LENGTH = 250
class BaseCache(object):
def __init__(self, params):
timeout = params.get('timeout', 300)
@@ -116,3 +124,21 @@ class BaseCache(object):
def clear(self):
"""Remove *all* values from the cache at once."""
raise NotImplementedError
def validate_key(self, key):
"""
Warn about keys that would not be portable to the memcached
backend. This encourages (but does not force) writing backend-portable
cache code.
"""
if len(key) > MEMCACHE_MAX_KEY_LENGTH:
warnings.warn('Cache key will cause errors if used with memcached: '
'%s (longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH),
CacheKeyWarning)
for char in key:
if ord(char) < 33 or ord(char) == 127:
warnings.warn('Cache key contains characters that will cause '
'errors if used with memcached: %r' % key,
CacheKeyWarning)

View File

@@ -46,6 +46,7 @@ class CacheClass(BaseCache):
self._cull_frequency = 3
def get(self, key, default=None):
self.validate_key(key)
db = router.db_for_read(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
cursor = connections[db].cursor()
@@ -65,9 +66,11 @@ class CacheClass(BaseCache):
return pickle.loads(base64.decodestring(value))
def set(self, key, value, timeout=None):
self.validate_key(key)
self._base_set('set', key, value, timeout)
def add(self, key, value, timeout=None):
self.validate_key(key)
return self._base_set('add', key, value, timeout)
def _base_set(self, mode, key, value, timeout=None):
@@ -103,6 +106,7 @@ class CacheClass(BaseCache):
return True
def delete(self, key):
self.validate_key(key)
db = router.db_for_write(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
cursor = connections[db].cursor()
@@ -111,6 +115,7 @@ class CacheClass(BaseCache):
transaction.commit_unless_managed(using=db)
def has_key(self, key):
self.validate_key(key)
db = router.db_for_read(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
cursor = connections[db].cursor()

View File

@@ -6,22 +6,25 @@ class CacheClass(BaseCache):
def __init__(self, *args, **kwargs):
pass
def add(self, *args, **kwargs):
def add(self, key, *args, **kwargs):
self.validate_key(key)
return True
def get(self, key, default=None):
self.validate_key(key)
return default
def set(self, *args, **kwargs):
pass
def set(self, key, *args, **kwargs):
self.validate_key(key)
def delete(self, *args, **kwargs):
pass
def delete(self, key, *args, **kwargs):
self.validate_key(key)
def get_many(self, *args, **kwargs):
return {}
def has_key(self, *args, **kwargs):
def has_key(self, key, *args, **kwargs):
self.validate_key(key)
return False
def set_many(self, *args, **kwargs):

View File

@@ -32,6 +32,7 @@ class CacheClass(BaseCache):
self._createdir()
def add(self, key, value, timeout=None):
self.validate_key(key)
if self.has_key(key):
return False
@@ -39,6 +40,7 @@ class CacheClass(BaseCache):
return True
def get(self, key, default=None):
self.validate_key(key)
fname = self._key_to_file(key)
try:
f = open(fname, 'rb')
@@ -56,6 +58,7 @@ class CacheClass(BaseCache):
return default
def set(self, key, value, timeout=None):
self.validate_key(key)
fname = self._key_to_file(key)
dirname = os.path.dirname(fname)
@@ -79,6 +82,7 @@ class CacheClass(BaseCache):
pass
def delete(self, key):
self.validate_key(key)
try:
self._delete(self._key_to_file(key))
except (IOError, OSError):
@@ -95,6 +99,7 @@ class CacheClass(BaseCache):
pass
def has_key(self, key):
self.validate_key(key)
fname = self._key_to_file(key)
try:
f = open(fname, 'rb')

View File

@@ -30,6 +30,7 @@ class CacheClass(BaseCache):
self._lock = RWLock()
def add(self, key, value, timeout=None):
self.validate_key(key)
self._lock.writer_enters()
try:
exp = self._expire_info.get(key)
@@ -44,6 +45,7 @@ class CacheClass(BaseCache):
self._lock.writer_leaves()
def get(self, key, default=None):
self.validate_key(key)
self._lock.reader_enters()
try:
exp = self._expire_info.get(key)
@@ -76,6 +78,7 @@ class CacheClass(BaseCache):
self._expire_info[key] = time.time() + timeout
def set(self, key, value, timeout=None):
self.validate_key(key)
self._lock.writer_enters()
# Python 2.4 doesn't allow combined try-except-finally blocks.
try:
@@ -87,6 +90,7 @@ class CacheClass(BaseCache):
self._lock.writer_leaves()
def has_key(self, key):
self.validate_key(key)
self._lock.reader_enters()
try:
exp = self._expire_info.get(key)
@@ -127,6 +131,7 @@ class CacheClass(BaseCache):
pass
def delete(self, key):
self.validate_key(key)
self._lock.writer_enters()
try:
self._delete(key)

View File

@@ -1,4 +1,9 @@
"Global Django exceptions"
"""
Global Django exception and warning classes.
"""
class DjangoRuntimeWarning(RuntimeWarning):
pass
class ObjectDoesNotExist(Exception):
"The requested object does not exist"