mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #32193 -- Deprecated MemcachedCache.
This commit is contained in:
		
							
								
								
									
										5
									
								
								django/core/cache/__init__.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								django/core/cache/__init__.py
									
									
									
									
										vendored
									
									
								
							| @@ -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() | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								django/core/cache/backends/memcached.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								django/core/cache/backends/memcached.py
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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
									
									
								
							
							
						
						
									
										50
									
								
								tests/cache/tests.py
									
									
									
									
										vendored
									
									
								
							| @@ -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, | ||||
|   | ||||
| @@ -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' | ||||
|   | ||||
| @@ -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__)) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user