-
-
Notifications
You must be signed in to change notification settings - Fork 437
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_finished
signal after every request (this has existed in Django since at least version 1.8) - The signal includes keyword arguments like
signal
andsender
- 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:
# ... implementation
Current 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.