Skip to content

Bug: TypeError in RedisCache.close() when called with signal parameter #787

@smaccona

Description

@smaccona

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:

  1. Django sends the request_finished signal after every request (this has existed in Django since at least version 1.8)
  2. The signal includes keyword arguments like signal and sender
  3. These are passed to the cache's close() method
  4. The cache then passes the same arguments to the cache's client, which by default is DefaultClient, and DefaultClient.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions