Help: Python, caching

Howdy,

I could use some help … I can not figure out why some code doesn’t seem to work.

I have created a global cache decorator. And it’s working as far as caching the values of the functions I wrap in it… but I need to be able to also clear an item from the cache (so I can update/replace it’s cached values.)

What is not working, the the deletion of the ‘key item’ from the cache dictionary, I have tried several ways of tring to remove the key and they all seem to fail. I am not sure what I am doing wrong.

here is the code for the cache:

############################################################################
### cache 
## -------------------------------------------------------------------------
global CacheDepot
CacheDepot = {}


# --function----------------------------------------------------------------
def cacheItem(function):
    global CacheDepot
    def wrapper(*args):
        if args in CacheDepot:
            return CacheDepot[args]
        else:
            rv = function(*args)
            CacheDepot[args] = rv
            return rv
    return wrapper

Here is an example of a simple function I cache:

@cacheItem            
def p4checkFileStats(filePath):
    p4checkResults = p4client.checkFileStats(filePath)
    return p4checkResults

and here is a wip function to remove an item from the cache based on it’s itemKey (using filePath for example):

# --function----------------------------------------------------------------
def cacheDelItem(itemKey):
    global CacheDepot
    
    try:
        CacheDepot.has_key(itemKey)
        print 'The item was in the cache.'
        pass
    except:
        print 'The itemKey is not in the cache'
        pass
    
    try:
        r = dict(CacheDepot)
        # r.__delitem__(itemKey)
        try:
            del r[itemKey]
        except KeyError:
            pass
        #keyPop = r.pop(itemKey, None)
        CacheDepot = r
        return True
    except:
        print 'Could not delete that itemKey'
        pass
        
    return None

So I am watching things in the debugger, I know the key is there … and it passes this check:

CacheDepot.has_key(itemKey)

but none of my attempts to try and remove it pass.

Anyone know what I am doing wrong?

In your try block, catch an exception and see what it is. Its hard to say what the problem is.

dict.has_key will never error, why is that in a try/except statement?

If you want to test dictionary membership, just use

if key in CacheDepot:
    # key exists
else:
    # key doesn't exist

I’m not sure why you have so many try/except statements. Also I would recommend always avoiding general try/excepts (where you aren’t specifying an exception type to catch), unless you want to do something with the traceback data or you really don’t care why it failed. General try/excepts like that are dangerous because they hide issues with your code–you should be as specific as possible with the exceptions you allow.

Your delete function can just be:

def cacheDelItem(itemKey):
    global CacheDepot
    if itemKey in CacheDepot:
        del CacheDepot[itemKey]
        return True

Agree with TheMaxx. The exceptions raised might help you work out what’s going wrong so don’t catch them all and throw them away.

Just a guess but I’d expect something like this to work:


def cacheDelItem(itemKey):
    global CacheDepot
    if itemKey in CacheDepot:
        del CacheDepot[itemKey]

Personally, I’d also consider grouping your cache dict and cache functionality into a class rather than using a global. You don’t know what else might overwrite the global so that could end up giving you weird bugs (if not now then later).

Edit: Beaten by capper once again :P. I’ll leave it for agreement’s sake :slight_smile:

actually, it’s because I was trying out different was of approaching it … and seeing what might pass/fail.

def cacheDelItem(itemKey):
global CacheDepot
if itemKey in CacheDepot:
del CacheDepot[itemKey]

This was the first thing I tried, but watching in the debugger the itemkey never actually got deleted.

[QUOTE=Warheart;22350]Agree with TheMaxx. The exceptions raised might help you work out what’s going wrong so don’t catch them all and throw them away.

Just a guess but I’d expect something like this to work:


def cacheDelItem(itemKey):
    global CacheDepot
    if itemKey in CacheDepot:
        del CacheDepot[itemKey]

Personally, I’d also consider grouping your cache dict and cache functionality into a class rather than using a global. You don’t know what else might overwrite the global so that could end up giving you weird bugs (if not now then later).

Edit: Beaten by capper once again :P. I’ll leave it for agreement’s sake :)[/QUOTE]

I was trying to figure out a method of a shared cache across some modules (which all act on some paths).
so I put my code in a cache.py file, then I created a cache:

global CacheDepot
CacheDepot = {}

Then I could import that same CacheDepot into any other place I wanted to share it.

I know it’s probably not the best way to do it, and I was planning on upgrading it to something more robust … but in the short term I was just playing with code and trying something new I have never done before.

I don’t have a lot of experience with global caching, but my first instinct is that your current method isn’t restrictive enough. What if you have two functions that take the same argument, say a filename, but one returns a p4 fstat and the other returns the root directory of the character or environment the file is in? What about using a local cache for each function? Or at least storing the cached keys with a little more granularity, like under the calling function’s object, or in categories like ‘fstat’.

I feel like the implementation will depend a lot on how you anticipate using it–like whether it’s just for caching perforce results, or intended as something a bit more general that persists between different modules (which it sounds to be). Most caching implementations I’ve seen/used (like a lazy-loading descriptor) as restricted to classes/specific data types.

yes, all things to consider … this experiment, I was pretty specific in what I wanted to do… cache perforce fstats. And I realized in messing around that I even had to make sure that the item I was caching was hashable. etc. early on, I ended up with a dict that ended up looking like it have many multiple/duplicate keys (I know that doesn’t make sense). Anyway, I decided that I was just going to deal with this one specific case (path, fstats) and see if I could get a dirt simple solution implemented/working (not a final solution.) I still have zero clue why I couldn’t get keys to delete properly in that version, even though I could verify if that value was a key in the dict.

Anyway, I have a much better solution working now - per function cache decorator, with a max size queue, clear, delete, etc.
And so via imports, I can use a cached function across multiple modules and have it be shared in an app. It’s much nicer and actually easier to debug then a generic global cache.

I appreciate the input and help.

My delete function now looks like this:

def delItemKey(*args, **kwargs):
            itemkey = args
            if kwargs:
                itemkey += (kwarg_mark,) + tuple(sorted(kwargs.items()))
                
            if itemkey in CacheDepot:
                del CacheDepot[itemkey]
                return True

and I am using the same implementation to build the original key, and this works great.