summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-09-16 09:37:03 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-09-20 21:23:01 +0200
commit05f3a6186efefc9fca2204a745b992501c6fd91f (patch)
tree15dd3eca3d6798c91a3bba3f6c89a527e06c53af
parente2be307b3ab6ebf339b3a765fe64967c9602266f (diff)
Refs #32193 -- Removed MemcachedCache per deprecation timeline.
-rw-r--r--django/core/cache/backends/memcached.py40
-rw-r--r--docs/releases/4.1.txt3
-rw-r--r--docs/topics/cache.txt12
-rw-r--r--tests/cache/tests.py108
-rwxr-xr-xtests/runtests.py6
5 files changed, 12 insertions, 157 deletions
diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py
index e5a7142849..472a28179c 100644
--- a/django/core/cache/backends/memcached.py
+++ b/django/core/cache/backends/memcached.py
@@ -1,14 +1,11 @@
"Memcached cache backend"
-import pickle
import re
import time
-import warnings
from django.core.cache.backends.base import (
DEFAULT_TIMEOUT, BaseCache, InvalidCacheKey, memcache_key_warnings,
)
-from django.utils.deprecation import RemovedInDjango41Warning
from django.utils.functional import cached_property
@@ -136,43 +133,6 @@ class BaseMemcachedCache(BaseCache):
raise InvalidCacheKey(warning)
-class MemcachedCache(BaseMemcachedCache):
- "An implementation of a cache binding using python-memcached"
-
- # python-memcached doesn't support default values in get().
- # https://github.com/linsomniac/python-memcached/issues/159
- _missing_key = None
-
- def __init__(self, server, params):
- warnings.warn(
- 'MemcachedCache is deprecated in favor of PyMemcacheCache and '
- 'PyLibMCCache.',
- RemovedInDjango41Warning, stacklevel=2,
- )
- # python-memcached ≥ 1.45 returns None for a nonexistent key in
- # incr/decr(), python-memcached < 1.45 raises ValueError.
- import memcache
- super().__init__(server, params, library=memcache, value_not_found_exception=ValueError)
- self._options = {'pickleProtocol': pickle.HIGHEST_PROTOCOL, **self._options}
-
- def get(self, key, default=None, version=None):
- key = self.make_and_validate_key(key, version=version)
- val = self._cache.get(key)
- # python-memcached doesn't support default values in get().
- # https://github.com/linsomniac/python-memcached/issues/159
- # Remove this method if that issue is fixed.
- if val is None:
- return default
- return val
-
- def delete(self, key, version=None):
- # python-memcached's delete() returns True when key doesn't exist.
- # https://github.com/linsomniac/python-memcached/issues/170
- # Call _deletetouch() without the NOT_FOUND in expected results.
- key = self.make_and_validate_key(key, version=version)
- return bool(self._cache._deletetouch([b'DELETED'], 'delete', key))
-
-
class PyLibMCCache(BaseMemcachedCache):
"An implementation of a cache binding using pylibmc"
def __init__(self, server, params):
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index 8cb4297d0e..157a05acdb 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -260,3 +260,6 @@ to remove usage of these features.
* ``TransactionTestCase.assertQuerysetEqual()`` no longer calls ``repr()`` on a
queryset when compared to string values.
+
+* The ``django.core.cache.backends.memcached.MemcachedCache`` backend is
+ removed.
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index f56bc00ce8..e9c3a025c8 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -158,12 +158,6 @@ permanent storage -- they're all intended to be solutions for caching, not
storage -- but we point this out here because memory-based caching is
particularly temporary.
-.. deprecated:: 3.2
-
- The ``MemcachedCache`` backend is deprecated as ``python-memcached`` has
- some problems and seems to be unmaintained. Use ``PyMemcacheCache`` or
- ``PyLibMCCache`` instead.
-
.. _redis:
Redis
@@ -931,12 +925,6 @@ stored a literal value ``None``, use a sentinel object as the default::
>>> cache.get('my_key', sentinel) is sentinel
True
-.. admonition:: ``MemcachedCache``
-
- Due to a ``python-memcached`` limitation, it's not possible to distinguish
- between stored ``None`` value and a cache miss signified by a return value
- of ``None`` on the deprecated ``MemcachedCache`` backend.
-
``cache.get()`` can take a ``default`` argument. This specifies which value to
return if the object doesn't exist in the cache::
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index b880662858..29e527d4e6 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -11,7 +11,6 @@ import tempfile
import threading
import time
import unittest
-import warnings
from pathlib import Path
from unittest import mock, skipIf
@@ -38,7 +37,7 @@ from django.template.context_processors import csrf
from django.template.response import TemplateResponse
from django.test import (
RequestFactory, SimpleTestCase, TestCase, TransactionTestCase,
- ignore_warnings, override_settings,
+ override_settings,
)
from django.test.signals import setting_changed
from django.test.utils import CaptureQueriesContext
@@ -46,7 +45,6 @@ from django.utils import timezone, translation
from django.utils.cache import (
get_cache_key, learn_cache_key, patch_cache_control, patch_vary_headers,
)
-from django.utils.deprecation import RemovedInDjango41Warning
from django.views.decorators.cache import cache_control, cache_page
from .models import Poll, expensive_calculation
@@ -285,10 +283,6 @@ class BaseCacheTests:
# A common set of tests to apply to all cache backends
factory = RequestFactory()
- # RemovedInDjango41Warning: python-memcached doesn't support .get() with
- # default.
- supports_get_with_default = True
-
# Some clients raise custom exceptions when .incr() or .decr() are called
# with a non-integer value.
incr_decr_type_error = TypeError
@@ -357,10 +351,7 @@ class BaseCacheTests:
cache.set("no_expiry", "here", None)
self.assertIs(cache.has_key("no_expiry"), True)
cache.set('null', None)
- self.assertIs(
- cache.has_key('null'),
- True if self.supports_get_with_default else False,
- )
+ self.assertIs(cache.has_key('null'), True)
def test_in(self):
# The in operator can be used to inspect cache contents
@@ -368,10 +359,7 @@ class BaseCacheTests:
self.assertIn("hello2", cache)
self.assertNotIn("goodbye2", cache)
cache.set('null', None)
- if self.supports_get_with_default:
- self.assertIn('null', cache)
- else:
- self.assertNotIn('null', cache)
+ self.assertIn('null', cache)
def test_incr(self):
# Cache values can be incremented
@@ -965,11 +953,7 @@ class BaseCacheTests:
cache.incr_version('does_not_exist')
cache.set('null', None)
- if self.supports_get_with_default:
- self.assertEqual(cache.incr_version('null'), 2)
- else:
- with self.assertRaises(self.incr_decr_type_error):
- cache.incr_version('null')
+ self.assertEqual(cache.incr_version('null'), 2)
def test_decr_version(self):
cache.set('answer', 42, version=2)
@@ -996,11 +980,7 @@ class BaseCacheTests:
cache.decr_version('does_not_exist', version=2)
cache.set('null', None, version=2)
- if self.supports_get_with_default:
- self.assertEqual(cache.decr_version('null', version=2), 1)
- else:
- with self.assertRaises(self.incr_decr_type_error):
- cache.decr_version('null', version=2)
+ self.assertEqual(cache.decr_version('null', version=2), 1)
def test_custom_key_func(self):
# Two caches with different key functions aren't visible to each other
@@ -1059,11 +1039,8 @@ class BaseCacheTests:
self.assertEqual(cache.get_or_set('projector', 42), 42)
self.assertEqual(cache.get('projector'), 42)
self.assertIsNone(cache.get_or_set('null', None))
- if self.supports_get_with_default:
- # Previous get_or_set() stores None in the cache.
- self.assertIsNone(cache.get('null', 'default'))
- else:
- self.assertEqual(cache.get('null', 'default'), 'default')
+ # Previous get_or_set() stores None in the cache.
+ self.assertIsNone(cache.get('null', 'default'))
def test_get_or_set_callable(self):
def my_callable():
@@ -1073,11 +1050,8 @@ class BaseCacheTests:
self.assertEqual(cache.get_or_set('mykey', my_callable()), 'value')
self.assertIsNone(cache.get_or_set('null', lambda: None))
- if self.supports_get_with_default:
- # Previous get_or_set() stores None in the cache.
- self.assertIsNone(cache.get('null', 'default'))
- else:
- self.assertEqual(cache.get('null', 'default'), 'default')
+ # Previous get_or_set() stores None in the cache.
+ self.assertIsNone(cache.get('null', 'default'))
def test_get_or_set_version(self):
msg = "get_or_set() missing 1 required positional argument: 'default'"
@@ -1504,70 +1478,6 @@ class BaseMemcachedTests(BaseCacheTests):
self.assertEqual(failing_keys, ['key'])
-# RemovedInDjango41Warning.
-MemcachedCache_params = configured_caches.get('django.core.cache.backends.memcached.MemcachedCache')
-
-
-@ignore_warnings(category=RemovedInDjango41Warning)
-@unittest.skipUnless(MemcachedCache_params, "MemcachedCache backend not configured")
-@override_settings(CACHES=caches_setting_for_tests(
- base=MemcachedCache_params,
- exclude=memcached_excluded_caches,
-))
-class MemcachedCacheTests(BaseMemcachedTests, TestCase):
- base_params = MemcachedCache_params
- supports_get_with_default = False
- incr_decr_type_error = ValueError
-
- def test_memcached_uses_highest_pickle_version(self):
- # Regression test for #19810
- for cache_key in settings.CACHES:
- with self.subTest(cache_key=cache_key):
- self.assertEqual(caches[cache_key]._cache.pickleProtocol, pickle.HIGHEST_PROTOCOL)
-
- @override_settings(CACHES=caches_setting_for_tests(
- base=MemcachedCache_params,
- exclude=memcached_excluded_caches,
- OPTIONS={'server_max_value_length': 9999},
- ))
- def test_memcached_options(self):
- self.assertEqual(cache._cache.server_max_value_length, 9999)
-
- def test_default_used_when_none_is_set(self):
- """
- python-memcached doesn't support default in get() so this test
- overrides the one in BaseCacheTests.
- """
- cache.set('key_default_none', None)
- self.assertEqual(cache.get('key_default_none', default='default'), 'default')
-
-
-class MemcachedCacheDeprecationTests(SimpleTestCase):
- def test_warning(self):
- from django.core.cache.backends.memcached import MemcachedCache
-
- # Remove warnings filter on MemcachedCache deprecation warning, added
- # in runtests.py.
- warnings.filterwarnings(
- 'error',
- 'MemcachedCache is deprecated',
- category=RemovedInDjango41Warning,
- )
- try:
- msg = (
- 'MemcachedCache is deprecated in favor of PyMemcacheCache and '
- 'PyLibMCCache.'
- )
- with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
- MemcachedCache('127.0.0.1:11211', {})
- finally:
- warnings.filterwarnings(
- 'ignore',
- 'MemcachedCache is deprecated',
- category=RemovedInDjango41Warning,
- )
-
-
@unittest.skipUnless(PyLibMCCache_params, "PyLibMCCache backend not configured")
@override_settings(CACHES=caches_setting_for_tests(
base=PyLibMCCache_params,
diff --git a/tests/runtests.py b/tests/runtests.py
index f922bcd44d..7e08baa5c0 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -48,12 +48,6 @@ warnings.simplefilter("error", ResourceWarning)
warnings.simplefilter("error", RuntimeWarning)
# Ignore known warnings in test dependencies.
warnings.filterwarnings("ignore", "'U' mode is deprecated", DeprecationWarning, module='docutils.io')
-# RemovedInDjango41Warning: Ignore MemcachedCache deprecation warning.
-warnings.filterwarnings(
- 'ignore',
- 'MemcachedCache is deprecated',
- category=RemovedInDjango41Warning,
-)
# Reduce garbage collection frequency to improve performance. Since CPython
# uses refcounting, garbage collection only collects objects with cyclic