summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2021-12-13 17:09:13 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-12-14 08:46:16 +0100
commit3b03bce122b0f7336a90e3cc111a717c1a8f3700 (patch)
treed7ca770172bce7ca5ee25aa7ecddab28ff6a9b98
parentcc5bbd447bec57165d5bdf062128fdee0df81ee4 (diff)
[4.0.x] Fixed #33361 -- Fixed Redis cache backend crash on booleans.
Backport of 2f33217ea2cad688040dd6044cdda946c62e5b65 from main
-rw-r--r--django/core/cache/backends/redis.py8
-rw-r--r--docs/releases/4.0.1.txt3
-rw-r--r--tests/cache/tests.py14
3 files changed, 21 insertions, 4 deletions
diff --git a/django/core/cache/backends/redis.py b/django/core/cache/backends/redis.py
index 16556b1ded..ec2fb06102 100644
--- a/django/core/cache/backends/redis.py
+++ b/django/core/cache/backends/redis.py
@@ -10,8 +10,14 @@ from django.utils.module_loading import import_string
class RedisSerializer(PickleSerializer):
+ """
+ Similar to PickSerializer, except integers are serialized as native Redis
+ integers for better incr() and decr() atomicity.
+ """
def dumps(self, obj):
- if isinstance(obj, int):
+ # Only skip pickling for integers, a int subclasses as bool should be
+ # pickled.
+ if type(obj) is int:
return obj
return super().dumps(obj)
diff --git a/docs/releases/4.0.1.txt b/docs/releases/4.0.1.txt
index fa4ebe826f..08e5b206bb 100644
--- a/docs/releases/4.0.1.txt
+++ b/docs/releases/4.0.1.txt
@@ -12,3 +12,6 @@ Bugfixes
* Fixed a regression in Django 4.0 that caused a crash of
:meth:`~django.test.SimpleTestCase.assertFormsetError` on a formset named
``form`` (:ticket:`33346`).
+
+* Fixed a bug in Django 4.0 that caused a crash on booleans with the
+ ``RedisCache`` backend (:ticket:`33361`).
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index b880662858..70cdefbf39 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -411,17 +411,20 @@ class BaseCacheTests:
def test_data_types(self):
# Many different data types can be cached
- stuff = {
+ tests = {
'string': 'this is a string',
'int': 42,
+ 'bool': True,
'list': [1, 2, 3, 4],
'tuple': (1, 2, 3, 4),
'dict': {'A': 1, 'B': 2},
'function': f,
'class': C,
}
- cache.set("stuff", stuff)
- self.assertEqual(cache.get("stuff"), stuff)
+ for key, value in tests.items():
+ with self.subTest(key=key):
+ cache.set(key, value)
+ self.assertEqual(cache.get(key), value)
def test_cache_read_for_model_instance(self):
# Don't want fields with callable as default to be called on cache read
@@ -1785,6 +1788,11 @@ class RedisCacheTests(BaseCacheTests, TestCase):
def test_get_client(self):
self.assertIsInstance(cache._cache.get_client(), self.lib.Redis)
+ def test_serializer_dumps(self):
+ self.assertEqual(cache._cache._serializer.dumps(123), 123)
+ self.assertIsInstance(cache._cache._serializer.dumps(True), bytes)
+ self.assertIsInstance(cache._cache._serializer.dumps('abc'), bytes)
+
class FileBasedCachePathLibTests(FileBasedCacheTests):
def mkdtemp(self):