def _get_namespace_prefix(namespace): ''' Gets (or sets if uninitialized) the key prefix for the given namespace string. The return value will prepend any keys that belong to the namespace. ''' ns_key = cache.get(namespace) if not ns_key: ns_key = _make_namespace_prefix() cache.set(namespace, ns_key) # Compact the key before returning it to save space when using it. return pack_int(ns_key)
def _make_key_args_from_function(func, *args, **kwargs): ''' Add a bunch of hopefully uniquely identifying parameters to a list to be passed to `make_key`. It's pretty crafty in finding distinguishing params to use, but it is slightly conservative so as to not overrun the memcached key length limit, which results in a non-human-readable hash for a key. ''' key_args = ['cached_func', func.__name__] # This works on both functions and class methods. signature_args = inspect.getargspec(func).args if (inspect.ismethod(func) or (signature_args and signature_args[0] in ['self', 'cls'])): # Method, probably. # # If ismethod returns True, it's definitely a method. Otherwise, # we have to guess based on the first arg of the function's signature. # This is the best guess we can have in Python, because the way a # function is passed to a decorator inside a class definition, the # decorated function is as yet neither a bound nor unbound method. It's # just a regular function. So we must guess from its args. # # A guess is good enough, since it only means that we add some extra # fields from the first arg. If we're wrong, the key is more # conservative (more detailed) than need be. We could wrongly call it a # function when it's actually a method, but only if they're doing # unsightly things like naming the "self" or "cls" arg something else. self = args[0] key_args.append(self.__class__.__name__) if hasattr(self, 'pk'): # django model? `pk` is a great differentiator! key_args.append(self.pk) key_args.extend(args[1:]) else: # Function. key_args.extend(args) key_args.extend(kwargs.values()) # To be extra safe! (unreadable, so at end of key.) # If this results in any collisions, it actually won't make a difference. # It's fine to memoize functions that collide on this as if # they are one, since they're identical if their codeblock hash is the same. key_args.append(pack_int(func.__code__.__hash__())) return key_args