[Answered ]-`dir()` doesn't show the cache object attributes in Django

1👍

You can show all those methods from BaseCache and even see all other available attributes.

from django.core.cache.backends.base import BaseCache

dir(BaseCache)

['__class__', '__contains__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', 
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'__weakref__', '_missing_key', 'aadd', 'aclear', 'aclose', 'add', 'adecr', 'adecr_version', 
'adelete', 'adelete_many', 'aget', 'aget_many', 'aget_or_set', 'ahas_key', 'aincr', 
'aincr_version', 'aset', 'aset_many', 'atouch', 'clear', 'close', 'decr', 'decr_version', 
'delete', 'delete_many', 'get', 'get_backend_timeout', 'get_many', 'get_or_set', 'has_key', 
'incr', 'incr_version', 'make_and_validate_key', 'make_key', 'set', 'set_many', 'touch', 
'validate_key']

From source code doc,

Caching framework.

This package defines set of cache backends that all conform to a
simple API. In a nutshell, a cache is a set of values — which can be
any object that may be pickled — identified by string keys. For the
complete API, see the abstract BaseCache class in
django.core.cache.backends.base.

1👍

You can always have a look at the source code…

In this case it’s because the cache value that you import from here is actually a ConnectionProxy object.

cache = ConnectionProxy(caches, DEFAULT_CACHE_ALIAS)

https://github.com/django/django/blob/main/django/utils/connection.py#L7

…and that object does not have any of those methods, instead it uses __getattr__ to ‘proxy’ uses of those attributes through to some other object instance.

    def __getattr__(self, item):
        return getattr(self._connections[self._alias], item)

Unless you set up some more complicated cache config, you can probably inspect those methods by doing:

dir(cache._connections["default"])

0👍

This is because the cache is a proxy to the default cache, indeed [GitHub]:

class ConnectionProxy:
    # …

    def __getattr__(self, item):
        return getattr(self._connections[self._alias], item)

    # …

It thus will for a method, which is an attribute, fallback on __getattr__, which will not then forward the item.

You can patch the __dir__ method through monkey patching:

from django.utils.connection import ConnectionProxy


def some_dir(self):
    return dir(self._connections[self._alias])


ConnectionProxy.__dir__ = some_dir

for example in the ready, this will then return the corresponding methods when using dir.

Update: I made a pull request [GitHub] to patch the behavior for the ConnectionProxy, although likely the same scenario will happen with all sorts of proxies and related design patterns throughout the code base.

-1👍

The reason you’re not seeing the cache methods when using the dir(cache) approach is that the cache methods are not directly part of the cache object itself. Instead, they are imported functions from the django.core.cache module. These functions are used to interact with the cache backend, but they are not instance methods of the cache object.

If you want to see the available cache methods, you can directly print out the functions themselves or create a custom list of method names. Here’s how you can do it:

from django.core.cache import cache

# Print out the cache methods cache_methods = [
    "set", "get", "touch", "incr", "decr", 
    "incr_version", "decr_version", "delete", 
    "delete_many", "clear", "close" ]

for method_name in cache_methods:
    method = getattr(cache, method_name)
    print(method_name, ":", method)

This will give you the output like:

set : <function cache.set at 0x...>
get : <function cache.get at 0x...>
touch : <function cache.touch at 0x...>
incr : <function cache.incr at 0x...>
decr : <function cache.decr at 0x...>
incr_version : <function cache.incr_version at 0x...>
decr_version : <function cache.decr_version at 0x...>
delete : <function cache.delete at 0x...>
delete_many : <function cache.delete_many at 0x...>
clear : <function cache.clear at 0x...>
close : <function cache.close at 0x...>

This way, you can see the actual functions that correspond to the cache methods. However, please note that the memory addresses (e.g., 0x…) will be different in your output.

Leave a comment