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