From 3b03bce122b0f7336a90e3cc111a717c1a8f3700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Mon, 13 Dec 2021 17:09:13 +0100 Subject: [PATCH] [4.0.x] Fixed #33361 -- Fixed Redis cache backend crash on booleans. Backport of 2f33217ea2cad688040dd6044cdda946c62e5b65 from main --- django/core/cache/backends/redis.py | 8 +++++++- docs/releases/4.0.1.txt | 3 +++ tests/cache/tests.py | 14 +++++++++++--- 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):