1"""Key functions for memoizing decorators.""" 2 3from __future__ import absolute_import 4 5__all__ = ('hashkey', 'typedkey') 6 7 8class _HashedTuple(tuple): 9 """A tuple that ensures that hash() will be called no more than once 10 per element, since cache decorators will hash the key multiple 11 times on a cache miss. See also _HashedSeq in the standard 12 library functools implementation. 13 14 """ 15 16 __hashvalue = None 17 18 def __hash__(self, hash=tuple.__hash__): 19 hashvalue = self.__hashvalue 20 if hashvalue is None: 21 self.__hashvalue = hashvalue = hash(self) 22 return hashvalue 23 24 def __add__(self, other, add=tuple.__add__): 25 return _HashedTuple(add(self, other)) 26 27 def __radd__(self, other, add=tuple.__add__): 28 return _HashedTuple(add(other, self)) 29 30 def __getstate__(self): 31 return {} 32 33 34# used for separating keyword arguments; we do not use an object 35# instance here so identity is preserved when pickling/unpickling 36_kwmark = (_HashedTuple,) 37 38 39def hashkey(*args, **kwargs): 40 """Return a cache key for the specified hashable arguments.""" 41 42 if kwargs: 43 return _HashedTuple(args + sum(sorted(kwargs.items()), _kwmark)) 44 else: 45 return _HashedTuple(args) 46 47 48def typedkey(*args, **kwargs): 49 """Return a typed cache key for the specified hashable arguments.""" 50 51 key = hashkey(*args, **kwargs) 52 key += tuple(type(v) for v in args) 53 key += tuple(type(v) for _, v in sorted(kwargs.items())) 54 return key 55