Ticket #6447: 6447_r13405_withdoc.diff

File 6447_r13405_withdoc.diff, 7.5 KB (added by Carl Meyer, 14 years ago)

same as previous patch, with added doc note re key restrictions

  • django/core/cache/backends/base.py

    diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
    a b  
    55class InvalidCacheBackendError(ImproperlyConfigured):
    66    pass
    77
     8class InvalidCacheKeyError(Exception):
     9    pass
     10
     11# memcached does not accept keys longer than 250 characters
     12MAX_KEY_LENGTH = 250
     13
    814class BaseCache(object):
    915    def __init__(self, params):
    1016        timeout = params.get('timeout', 300)
     
    116122    def clear(self):
    117123        """Remove *all* values from the cache at once."""
    118124        raise NotImplementedError
     125
     126    def _validate_key(self, key):
     127        """
     128        Enforce keys that would be valid on all backends, to keep cache code portable.
     129
     130        """
     131        if len(key) > MAX_KEY_LENGTH:
     132            raise InvalidCacheKeyError('Key is too long (max length %s)' % MAX_KEY_LENGTH)
     133        for char in key:
     134            if ord(char) < 33 or ord(char) == 127:
     135                raise InvalidCacheKeyError('Key contains control character')
     136       
  • django/core/cache/backends/db.py

    diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
    a b  
    2525            self._cull_frequency = 3
    2626
    2727    def get(self, key, default=None):
     28        self._validate_key(key)
    2829        cursor = connection.cursor()
    2930        cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
    3031        row = cursor.fetchone()
     
    3940        return pickle.loads(base64.decodestring(value))
    4041
    4142    def set(self, key, value, timeout=None):
     43        self._validate_key(key)
    4244        self._base_set('set', key, value, timeout)
    4345
    4446    def add(self, key, value, timeout=None):
     47        self._validate_key(key)
    4548        return self._base_set('add', key, value, timeout)
    4649
    4750    def _base_set(self, mode, key, value, timeout=None):
     
    7477            return True
    7578
    7679    def delete(self, key):
     80        self._validate_key(key)
    7781        cursor = connection.cursor()
    7882        cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
    7983        transaction.commit_unless_managed()
    8084
    8185    def has_key(self, key):
     86        self._validate_key(key)
    8287        now = datetime.now().replace(microsecond=0)
    8388        cursor = connection.cursor()
    8489        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table,
  • django/core/cache/backends/dummy.py

    diff --git a/django/core/cache/backends/dummy.py b/django/core/cache/backends/dummy.py
    a b  
    66    def __init__(self, *args, **kwargs):
    77        pass
    88
    9     def add(self, *args, **kwargs):
     9    def add(self, key, *args, **kwargs):
     10        self._validate_key(key)
    1011        return True
    1112
    1213    def get(self, key, default=None):
     14        self._validate_key(key)
    1315        return default
    1416
    15     def set(self, *args, **kwargs):
    16         pass
     17    def set(self, key, *args, **kwargs):
     18        self._validate_key(key)
    1719
    18     def delete(self, *args, **kwargs):
    19         pass
     20    def delete(self, key, *args, **kwargs):
     21        self._validate_key(key)
    2022
    2123    def get_many(self, *args, **kwargs):
    2224        return {}
    2325
    24     def has_key(self, *args, **kwargs):
     26    def has_key(self, key, *args, **kwargs):
     27        self._validate_key(key)
    2528        return False
    2629
    2730    def set_many(self, *args, **kwargs):
  • django/core/cache/backends/filebased.py

    diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py
    a b  
    3232            self._createdir()
    3333
    3434    def add(self, key, value, timeout=None):
     35        self._validate_key(key)
    3536        if self.has_key(key):
    3637            return False
    3738
     
    3940        return True
    4041
    4142    def get(self, key, default=None):
     43        self._validate_key(key)
    4244        fname = self._key_to_file(key)
    4345        try:
    4446            f = open(fname, 'rb')
     
    5658        return default
    5759
    5860    def set(self, key, value, timeout=None):
     61        self._validate_key(key)
    5962        fname = self._key_to_file(key)
    6063        dirname = os.path.dirname(fname)
    6164
     
    7982            pass
    8083
    8184    def delete(self, key):
     85        self._validate_key(key)
    8286        try:
    8387            self._delete(self._key_to_file(key))
    8488        except (IOError, OSError):
     
    9599            pass
    96100
    97101    def has_key(self, key):
     102        self._validate_key(key)
    98103        fname = self._key_to_file(key)
    99104        try:
    100105            f = open(fname, 'rb')
  • django/core/cache/backends/locmem.py

    diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py
    a b  
    3030        self._lock = RWLock()
    3131
    3232    def add(self, key, value, timeout=None):
     33        self._validate_key(key)
    3334        self._lock.writer_enters()
    3435        try:
    3536            exp = self._expire_info.get(key)
     
    4445            self._lock.writer_leaves()
    4546
    4647    def get(self, key, default=None):
     48        self._validate_key(key)
    4749        self._lock.reader_enters()
    4850        try:
    4951            exp = self._expire_info.get(key)
     
    7678        self._expire_info[key] = time.time() + timeout
    7779
    7880    def set(self, key, value, timeout=None):
     81        self._validate_key(key)
    7982        self._lock.writer_enters()
    8083        # Python 2.4 doesn't allow combined try-except-finally blocks.
    8184        try:
     
    8790            self._lock.writer_leaves()
    8891
    8992    def has_key(self, key):
     93        self._validate_key(key)
    9094        self._lock.reader_enters()
    9195        try:
    9296            exp = self._expire_info.get(key)
     
    127131            pass
    128132
    129133    def delete(self, key):
     134        self._validate_key(key)
    130135        self._lock.writer_enters()
    131136        try:
    132137            self._delete(key)
  • docs/topics/cache.txt

    diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
    a b  
    496496    >>> cache.get('my_key')
    497497    'hello, world!'
    498498
     499.. note::
     500
     501   Cache keys may not be longer than 250 characters, and may not
     502   contain whitespace or control characters.
     503
    499504The ``timeout`` argument is optional and defaults to the ``timeout``
    500505argument in the ``CACHE_BACKEND`` setting (explained above). It's the number of
    501506seconds the value should be stored in the cache.
  • tests/regressiontests/cache/tests.py

    diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
    a b  
    352352        self.assertEqual(self.cache.get('key3'), 'sausage')
    353353        self.assertEqual(self.cache.get('key4'), 'lobster bisque')
    354354
     355    def test_invalid_keys(self):
     356        """
     357        All the builtin backends should refuse keys that would be refused by
     358        memcached, so code using the cache API is portable. Refs #6447.
     359
     360        We assert only that a generic exception is raised because we don't
     361        want to encumber the memcached backend with key-checking code, and the
     362        specific exceptions raised from memcached will depend on the client
     363        library used.
     364       
     365        """
     366        self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value')
     367        # memcached also limits key length to 250
     368        self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value')
     369
    355370class DBCacheTests(unittest.TestCase, BaseCacheTests):
    356371    def setUp(self):
    357372        # Spaces are used in the table name to ensure quoting/escaping is working
Back to Top