Fixed #32193 -- Deprecated MemcachedCache.

This commit is contained in:
Mariusz Felisiak 2020-12-09 21:27:32 +01:00 committed by GitHub
parent 2c5d6dc447
commit 5ce31d6a71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 78 additions and 40 deletions

View File

@ -50,9 +50,8 @@ cache = ConnectionProxy(caches, DEFAULT_CACHE_ALIAS)
def close_caches(**kwargs):
# Some caches -- python-memcached in particular -- need to do a cleanup at the
# end of a request cycle. If not implemented in a particular backend
# cache.close is a no-op
# Some caches need to do a cleanup at the end of a request cycle. If not
# implemented in a particular backend cache.close() is a no-op.
for cache in caches.all():
cache.close()

View File

@ -3,10 +3,12 @@
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
@ -164,6 +166,11 @@ class BaseMemcachedCache(BaseCache):
class MemcachedCache(BaseMemcachedCache):
"An implementation of a cache binding using python-memcached"
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

View File

@ -29,6 +29,8 @@ details on these changes.
* ``TransactionTestCase.assertQuerysetEqual()` will no longer automatically
call ``repr()`` on a queryset when compared to string values.
* ``django.core.cache.backends.memcached.MemcachedCache`` will be removed.
.. _deprecation-removed-in-4.0:
4.0

View File

@ -156,9 +156,8 @@ The cache backend to use. The built-in cache backends are:
* ``'django.core.cache.backends.dummy.DummyCache'``
* ``'django.core.cache.backends.filebased.FileBasedCache'``
* ``'django.core.cache.backends.locmem.LocMemCache'``
* ``'django.core.cache.backends.memcached.MemcachedCache'``
* ``'django.core.cache.backends.memcached.PyLibMCCache'``
* ``'django.core.cache.backends.memcached.PyMemcacheCache'``
* ``'django.core.cache.backends.memcached.PyLibMCCache'``
You can use a cache backend that doesn't ship with Django by setting
:setting:`BACKEND <CACHES-BACKEND>` to a fully-qualified path of a cache

View File

@ -660,3 +660,8 @@ Miscellaneous
``TransactionTestCase.assertQuerysetEqual()``, when compared to string
values, is deprecated. If you need the previous behavior, explicitly set
``transform`` to ``repr``.
* The ``django.core.cache.backends.memcached.MemcachedCache`` backend is
deprecated as ``python-memcached`` has some problems and seems to be
unmaintained. Use ``django.core.cache.backends.memcached.PyMemcacheCache``
or ``django.core.cache.backends.memcached.PyLibMCCache`` instead.

View File

@ -77,18 +77,16 @@ database or filesystem usage.
After installing Memcached itself, you'll need to install a Memcached
binding. There are several Python Memcached bindings available; the
three most common are `python-memcached`_, `pylibmc`_, and `pymemcache`_.
two supported by Django are `pylibmc`_ and `pymemcache`_.
.. _`python-memcached`: https://pypi.org/project/python-memcached/
.. _`pylibmc`: https://pypi.org/project/pylibmc/
.. _`pymemcache`: https://pypi.org/project/pymemcache/
To use Memcached with Django:
* Set :setting:`BACKEND <CACHES-BACKEND>` to
``django.core.cache.backends.memcached.MemcachedCache``,
``django.core.cache.backends.memcached.PyLibMCCache``, or
``django.core.cache.backends.memcached.PyMemcacheCache`` (depending on your
``django.core.cache.backends.memcached.PyMemcacheCache`` or
``django.core.cache.backends.memcached.PyLibMCCache`` (depending on your
chosen memcached binding)
* Set :setting:`LOCATION <CACHES-LOCATION>` to ``ip:port`` values,
@ -97,21 +95,21 @@ To use Memcached with Django:
``path`` is the path to a Memcached Unix socket file.
In this example, Memcached is running on localhost (127.0.0.1) port 11211, using
the ``python-memcached`` binding::
the ``pymemcache`` binding::
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}
In this example, Memcached is available through a local Unix socket file
:file:`/tmp/memcached.sock` using the ``python-memcached`` binding::
:file:`/tmp/memcached.sock` using the ``pymemcache`` binding::
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
@ -129,7 +127,7 @@ address 172.19.26.240 and 172.19.26.242, both on port 11211::
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
@ -143,7 +141,7 @@ on the IP addresses 172.19.26.240 (port 11211), 172.19.26.242 (port 11212), and
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11212',
@ -165,6 +163,12 @@ particularly temporary.
The ``PyMemcacheCache`` backend was added.
.. deprecated:: 3.2
The ``MemcachedCache`` backend is deprecated as ``python-memcached`` has
some problems and seems to be unmaintained. Use ``PyMemcacheCache`` or
``PyLibMCCache`` instead.
.. _database-caching:
Database caching
@ -452,19 +456,6 @@ of 60 seconds, and a maximum capacity of 1000 items::
}
}
Here's an example configuration for a ``python-memcached`` based backend with
an object size limit of 2MB::
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'server_max_value_length': 1024 * 1024 * 2,
}
}
}
Here's an example configuration for a ``pylibmc`` based backend that enables
the binary protocol, SASL authentication, and the ``ketama`` behavior mode::

50
tests/cache/tests.py vendored
View File

@ -11,6 +11,7 @@ import tempfile
import threading
import time
import unittest
import warnings
from pathlib import Path
from unittest import mock, skipIf
@ -35,13 +36,14 @@ from django.template.context_processors import csrf
from django.template.response import TemplateResponse
from django.test import (
RequestFactory, SimpleTestCase, TestCase, TransactionTestCase,
override_settings,
ignore_warnings, override_settings,
)
from django.test.signals import setting_changed
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
@ -1276,7 +1278,6 @@ configured_caches = {}
for _cache_params in settings.CACHES.values():
configured_caches[_cache_params['BACKEND']] = _cache_params
MemcachedCache_params = configured_caches.get('django.core.cache.backends.memcached.MemcachedCache')
PyLibMCCache_params = configured_caches.get('django.core.cache.backends.memcached.PyLibMCCache')
PyMemcacheCache_params = configured_caches.get('django.core.cache.backends.memcached.PyMemcacheCache')
@ -1349,10 +1350,7 @@ class BaseMemcachedTests(BaseCacheTests):
# By default memcached allows objects up to 1MB. For the cache_db session
# backend to always use the current session, memcached needs to delete
# the old key if it fails to set.
# pylibmc doesn't seem to have SERVER_MAX_VALUE_LENGTH as far as I can
# tell from a quick check of its source code. This is falling back to
# the default value exposed by python-memcached on my system.
max_value_length = getattr(cache._lib, 'SERVER_MAX_VALUE_LENGTH', 1048576)
max_value_length = 2 ** 20
cache.set('small_value', 'a')
self.assertEqual(cache.get('small_value'), 'a')
@ -1361,11 +1359,10 @@ class BaseMemcachedTests(BaseCacheTests):
try:
cache.set('small_value', large_value)
except Exception:
# Some clients (e.g. pylibmc) raise when the value is too large,
# while others (e.g. python-memcached) intentionally return True
# indicating success. This test is primarily checking that the key
# was deleted, so the return/exception behavior for the set()
# itself is not important.
# Most clients (e.g. pymemcache or pylibmc) raise when the value is
# too large. This test is primarily checking that the key was
# deleted, so the return/exception behavior for the set() itself is
# not important.
pass
# small_value should be deleted, or set if configured to accept larger values
value = cache.get('small_value')
@ -1390,6 +1387,11 @@ 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,
@ -1421,6 +1423,32 @@ class MemcachedCacheTests(BaseMemcachedTests, TestCase):
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,

View File

@ -9,6 +9,7 @@ Pillow >= 6.2.0
# pylibmc/libmemcached can't be built on Windows.
pylibmc; sys.platform != 'win32'
pymemcache >= 3.4.0
# RemovedInDjango41Warning.
python-memcached >= 1.59
pytz
pywatchman; sys.platform != 'win32'

View File

@ -47,6 +47,12 @@ 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,
)
RUNTESTS_DIR = os.path.abspath(os.path.dirname(__file__))