#!/usr/bin/env python import inspect, os, cPickle #import inspect, os, pickle def cachefn( pickledir="pickles", retval=None, args=None): callee_stack = inspect.stack()[1] callee_name = callee_stack[3] # if no arguments are passed, use all from the calling function # FIXME: this probably breaks when there are non-hashable arguments # FIXME: None is not guaranteed to hash the same. It depends # upon a memory address that changes, by instance and machine. # We'll substitute the string "None" for None objects. Of course, # this could pose a problem when both None and "None" are # reasonable arguments. # It's difficult to replace None in every possible object, # recursively. Maybe it's just easier to use a string of all arguments #print "callee_name", callee_name #print "args", args #print "type(args)", type(args) if args is None: argvalues = inspect.getargvalues( callee_stack[0]) args = argvalues[0] params_dict = {} for arg in args: if arg == 'self': continue params_dict[ arg] = argvalues[3][arg] params_items = params_dict.items() params_items = [ "None" if a is None else a for a in params_items] params_items.sort() params_tup = tuple( params_items) params_str = str( params_tup) # WARNING: None hashes to id(), the address of an object. So, # at different runs, and especially on different machines, it # is possible for hashed objects that contain None to result # in different hashes. I'm not sure how to be more # deterministic about this. params_hash = str(hash( params_str)) # if args is a string, don't bother hashing. # WARNING: no spaces! elif type(args) is str: params_hash = args else: params_tup = tuple(args) params_str = str( params_tup) params_hash = str(hash( params_str)) fname = callee_name+'-'+params_hash+'.pickle' fpathname = os.path.join( pickledir, fname) # #if type(args) is not str: # print params_tup # print params_str, type(params_str) #print params_hash #print fpathname # store the pickle if retval: # FIXME: detect and warn about other access problems if not os.access( pickledir, os.W_OK): os.makedirs( pickledir) fd = open( fpathname, 'wb') cPickle.dump( retval, fd, protocol=2) fd.close() return None # load the pickle else: if os.access(fpathname, os.R_OK): #print "returning value" #print fpathname fd = open( fpathname, 'rb') retval = cPickle.load( fd) fd.close() return retval #print "returning None" return None