-
-
Notifications
You must be signed in to change notification settings - Fork 443
Description
Describe the bug
There's a bug in django-redis where the RedisCache.close() method accepts arbitrary keyword arguments and then passes them directly to self.client.close(), which by default (using DefaultClient) hasn't accepted any arguments since commit c7be6cc (November 1, 2023).
This causes a TypeError when Django's request_finished signal calls the cache's close() method with a signal parameter. My guess is nobody realized this because even though the commit was almost 2 years ago, it was only released in 6.0.0, and the prior release (5.4.0) was in October 1, 2023.
To Reproduce
When running Django under Gunicorn (but I expect other WSGI servers will have this issue too), this happens on every single request (see versions below). It also happens in development when using Django's runserver as a server. Here's what's happening:
- Django sends the
request_finishedsignal after every request (this has existed in Django since at least version 1.8) - The signal includes keyword arguments like
signalandsender - These are passed to the cache's
close()method - The cache then passes the same arguments to the cache's client, which by default is
DefaultClient, andDefaultClient.close()does not accept any keyword arguments, so we get an error
Expected behavior
I expected to not get an error on every request.
Stack trace
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/gunicorn/workers/sync.py", line 190, in handle_request
respiter.close()
File "/usr/local/lib/python3.9/site-packages/django/http/response.py", line 335, in close
signals.request_finished.send(sender=self._handler_class)
File "/usr/local/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 176, in send
return [
File "/usr/local/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/usr/local/lib/python3.9/site-packages/django_redis/cache.py", line 29, in _decorator
return method(self, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django_redis/cache.py", line 182, in close
self.client.close(**kwargs)
TypeError: close() got an unexpected keyword argument 'signal'
Environment (please complete the following information):
- Python version: 3.9+
- Django Redis Version: 6.0.0+
- Django Version: 4.2 (but I think this will apply to multiple versions)
- Redis Version: 7.0
- redis-py Version: 6.2.0
- gunicorn Version: 22.0.0 (but I think this will happen in other versions and other WSGI servers also)
Additional context
The issue is in django_redis/cache.py at line 182:
def close(self, **kwargs):
self.client.close(**kwargs)The RedisCache.close() method accepts **kwargs but passes them directly to self.client.close(). However, in commit c7be6cc, the DefaultClient.close() method signature changed to not accept any parameters:
def close(self) -> None:
# ... implementationCurrent Workaround
We're currently using this workaround class:
from django_redis.cache import RedisCache
class CompatibleRedisCache(RedisCache):
"""
Custom Redis cache backend compatible with django-redis 6.0.
Fixes TypeError in close() method when called with signal keyword argument
by Django's request_finished signal.
"""
def close(self, **kwargs):
"""Close cache connections, ignoring any extra keyword arguments."""
self.client.close()Proposed Fix
This is a pretty simple fix. I propose to change line 182 in django_redis/cache.py from:
def close(self, **kwargs):
self.client.close(**kwargs)... to:
def close(self, **kwargs):
self.client.close()I will open a PR for this change.