| Index: client/third_party/cachetools/keys.py | 
| diff --git a/client/third_party/cachetools/keys.py b/client/third_party/cachetools/keys.py | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..ba1e2fc2bee6d9b06096513d3bea6ec00d82bcc1 | 
| --- /dev/null | 
| +++ b/client/third_party/cachetools/keys.py | 
| @@ -0,0 +1,42 @@ | 
| +"""Key functions for memoizing decorators.""" | 
| + | 
| +from __future__ import absolute_import | 
| + | 
| +__all__ = ('hashkey', 'typedkey') | 
| + | 
| + | 
| +class _HashedTuple(tuple): | 
| + | 
| +    __hashvalue = None | 
| + | 
| +    def __hash__(self, hash=tuple.__hash__): | 
| +        hashvalue = self.__hashvalue | 
| +        if hashvalue is None: | 
| +            self.__hashvalue = hashvalue = hash(self) | 
| +        return hashvalue | 
| + | 
| +    def __add__(self, other, add=tuple.__add__): | 
| +        return _HashedTuple(add(self, other)) | 
| + | 
| +    def __radd__(self, other, add=tuple.__add__): | 
| +        return _HashedTuple(add(other, self)) | 
| + | 
| +_kwmark = (object(),) | 
| + | 
| + | 
| +def hashkey(*args, **kwargs): | 
| +    """Return a cache key for the specified hashable arguments.""" | 
| + | 
| +    if kwargs: | 
| +        return _HashedTuple(args + sum(sorted(kwargs.items()), _kwmark)) | 
| +    else: | 
| +        return _HashedTuple(args) | 
| + | 
| + | 
| +def typedkey(*args, **kwargs): | 
| +    """Return a typed cache key for the specified hashable arguments.""" | 
| + | 
| +    key = hashkey(*args, **kwargs) | 
| +    key += tuple(type(v) for v in args) | 
| +    key += tuple(type(v) for _, v in sorted(kwargs.items())) | 
| +    return key | 
|  |