| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 1 | """functools.py - Tools for working with functions and callable objects | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 2 | """ | 
|  | 3 | # Python module wrapper for _functools C module | 
|  | 4 | # to allow utilities written in Python to be added | 
|  | 5 | # to the functools module. | 
|  | 6 | # Written by Nick Coghlan <ncoghlan at gmail.com> | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 7 | # and Raymond Hettinger <python at rcn.com> | 
|  | 8 | #   Copyright (C) 2006-2010 Python Software Foundation. | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 9 | # See C source code for _functools credits/copyright | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 10 |  | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 11 | __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', | 
| Benjamin Peterson | 1017ae5 | 2010-09-10 23:35:52 +0000 | [diff] [blame] | 12 | 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 13 |  | 
| Guido van Rossum | 0919a1a | 2006-08-26 20:49:04 +0000 | [diff] [blame] | 14 | from _functools import partial, reduce | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 15 | from collections import namedtuple | 
| Raymond Hettinger | cbe8813 | 2010-08-14 22:22:10 +0000 | [diff] [blame] | 16 | try: | 
| Raymond Hettinger | fd54117 | 2013-03-01 03:47:57 -0800 | [diff] [blame] | 17 | from _thread import RLock | 
| Raymond Hettinger | cbe8813 | 2010-08-14 22:22:10 +0000 | [diff] [blame] | 18 | except: | 
| Raymond Hettinger | 409f663 | 2013-03-01 23:20:13 -0800 | [diff] [blame^] | 19 | class RLock: | 
|  | 20 | 'Dummy reentrant lock' | 
|  | 21 | def __enter__(self): pass | 
|  | 22 | def __exit__(self, exctype, excinst, exctb): pass | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 23 |  | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 24 |  | 
|  | 25 | ################################################################################ | 
|  | 26 | ### update_wrapper() and wraps() decorator | 
|  | 27 | ################################################################################ | 
|  | 28 |  | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 29 | # update_wrapper() and wraps() are tools to help write | 
|  | 30 | # wrapper functions that can handle naive introspection | 
|  | 31 |  | 
| Meador Inge | ff7f64c | 2011-12-11 22:37:31 -0600 | [diff] [blame] | 32 | WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', | 
|  | 33 | '__annotations__') | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 34 | WRAPPER_UPDATES = ('__dict__',) | 
|  | 35 | def update_wrapper(wrapper, | 
|  | 36 | wrapped, | 
|  | 37 | assigned = WRAPPER_ASSIGNMENTS, | 
|  | 38 | updated = WRAPPER_UPDATES): | 
|  | 39 | """Update a wrapper function to look like the wrapped function | 
|  | 40 |  | 
|  | 41 | wrapper is the function to be updated | 
|  | 42 | wrapped is the original function | 
|  | 43 | assigned is a tuple naming the attributes assigned directly | 
|  | 44 | from the wrapped function to the wrapper function (defaults to | 
|  | 45 | functools.WRAPPER_ASSIGNMENTS) | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 46 | updated is a tuple naming the attributes of the wrapper that | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 47 | are updated with the corresponding attribute from the wrapped | 
|  | 48 | function (defaults to functools.WRAPPER_UPDATES) | 
|  | 49 | """ | 
| Nick Coghlan | 9887683 | 2010-08-17 06:17:18 +0000 | [diff] [blame] | 50 | wrapper.__wrapped__ = wrapped | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 51 | for attr in assigned: | 
| Nick Coghlan | 9887683 | 2010-08-17 06:17:18 +0000 | [diff] [blame] | 52 | try: | 
|  | 53 | value = getattr(wrapped, attr) | 
|  | 54 | except AttributeError: | 
|  | 55 | pass | 
|  | 56 | else: | 
|  | 57 | setattr(wrapper, attr, value) | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 58 | for attr in updated: | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 59 | getattr(wrapper, attr).update(getattr(wrapped, attr, {})) | 
| Thomas Wouters | 73e5a5b | 2006-06-08 15:35:45 +0000 | [diff] [blame] | 60 | # Return the wrapper so this can be used as a decorator via partial() | 
|  | 61 | return wrapper | 
|  | 62 |  | 
|  | 63 | def wraps(wrapped, | 
|  | 64 | assigned = WRAPPER_ASSIGNMENTS, | 
|  | 65 | updated = WRAPPER_UPDATES): | 
|  | 66 | """Decorator factory to apply update_wrapper() to a wrapper function | 
|  | 67 |  | 
|  | 68 | Returns a decorator that invokes update_wrapper() with the decorated | 
|  | 69 | function as the wrapper argument and the arguments to wraps() as the | 
|  | 70 | remaining arguments. Default arguments are as for update_wrapper(). | 
|  | 71 | This is a convenience function to simplify applying partial() to | 
|  | 72 | update_wrapper(). | 
|  | 73 | """ | 
|  | 74 | return partial(update_wrapper, wrapped=wrapped, | 
|  | 75 | assigned=assigned, updated=updated) | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 76 |  | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 77 |  | 
|  | 78 | ################################################################################ | 
|  | 79 | ### total_ordering class decorator | 
|  | 80 | ################################################################################ | 
|  | 81 |  | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 82 | def total_ordering(cls): | 
| Georg Brandl | e5a2673 | 2010-05-19 21:06:36 +0000 | [diff] [blame] | 83 | """Class decorator that fills in missing ordering methods""" | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 84 | convert = { | 
| Raymond Hettinger | 23f9fc3 | 2011-01-08 07:01:56 +0000 | [diff] [blame] | 85 | '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), | 
|  | 86 | ('__le__', lambda self, other: self < other or self == other), | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 87 | ('__ge__', lambda self, other: not self < other)], | 
| Raymond Hettinger | 23f9fc3 | 2011-01-08 07:01:56 +0000 | [diff] [blame] | 88 | '__le__': [('__ge__', lambda self, other: not self <= other or self == other), | 
|  | 89 | ('__lt__', lambda self, other: self <= other and not self == other), | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 90 | ('__gt__', lambda self, other: not self <= other)], | 
| Raymond Hettinger | 23f9fc3 | 2011-01-08 07:01:56 +0000 | [diff] [blame] | 91 | '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), | 
|  | 92 | ('__ge__', lambda self, other: self > other or self == other), | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 93 | ('__le__', lambda self, other: not self > other)], | 
| Raymond Hettinger | 23f9fc3 | 2011-01-08 07:01:56 +0000 | [diff] [blame] | 94 | '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), | 
|  | 95 | ('__gt__', lambda self, other: self >= other and not self == other), | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 96 | ('__lt__', lambda self, other: not self >= other)] | 
|  | 97 | } | 
| Raymond Hettinger | 3255c63 | 2010-09-16 00:31:21 +0000 | [diff] [blame] | 98 | # Find user-defined comparisons (not those inherited from object). | 
| Raymond Hettinger | 1006bd4 | 2010-09-14 22:55:13 +0000 | [diff] [blame] | 99 | roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)] | 
| Raymond Hettinger | 56de7e2 | 2010-04-10 16:59:03 +0000 | [diff] [blame] | 100 | if not roots: | 
|  | 101 | raise ValueError('must define at least one ordering operation: < > <= >=') | 
|  | 102 | root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__ | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 103 | for opname, opfunc in convert[root]: | 
|  | 104 | if opname not in roots: | 
|  | 105 | opfunc.__name__ = opname | 
|  | 106 | opfunc.__doc__ = getattr(int, opname).__doc__ | 
|  | 107 | setattr(cls, opname, opfunc) | 
|  | 108 | return cls | 
|  | 109 |  | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 110 |  | 
|  | 111 | ################################################################################ | 
|  | 112 | ### cmp_to_key() function converter | 
|  | 113 | ################################################################################ | 
|  | 114 |  | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 115 | def cmp_to_key(mycmp): | 
| Georg Brandl | e5a2673 | 2010-05-19 21:06:36 +0000 | [diff] [blame] | 116 | """Convert a cmp= function into a key= function""" | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 117 | class K(object): | 
| Raymond Hettinger | a0d1d96 | 2011-03-21 17:50:28 -0700 | [diff] [blame] | 118 | __slots__ = ['obj'] | 
| Raymond Hettinger | 7ab9e22 | 2011-04-05 02:33:54 -0700 | [diff] [blame] | 119 | def __init__(self, obj): | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 120 | self.obj = obj | 
|  | 121 | def __lt__(self, other): | 
|  | 122 | return mycmp(self.obj, other.obj) < 0 | 
|  | 123 | def __gt__(self, other): | 
|  | 124 | return mycmp(self.obj, other.obj) > 0 | 
|  | 125 | def __eq__(self, other): | 
|  | 126 | return mycmp(self.obj, other.obj) == 0 | 
|  | 127 | def __le__(self, other): | 
|  | 128 | return mycmp(self.obj, other.obj) <= 0 | 
|  | 129 | def __ge__(self, other): | 
|  | 130 | return mycmp(self.obj, other.obj) >= 0 | 
|  | 131 | def __ne__(self, other): | 
|  | 132 | return mycmp(self.obj, other.obj) != 0 | 
| Raymond Hettinger | 003be52 | 2011-05-03 11:01:32 -0700 | [diff] [blame] | 133 | __hash__ = None | 
| Raymond Hettinger | c50846a | 2010-04-05 18:56:31 +0000 | [diff] [blame] | 134 | return K | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 135 |  | 
| Raymond Hettinger | 7ab9e22 | 2011-04-05 02:33:54 -0700 | [diff] [blame] | 136 | try: | 
|  | 137 | from _functools import cmp_to_key | 
|  | 138 | except ImportError: | 
|  | 139 | pass | 
|  | 140 |  | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 141 |  | 
|  | 142 | ################################################################################ | 
|  | 143 | ### LRU Cache function decorator | 
|  | 144 | ################################################################################ | 
|  | 145 |  | 
| Raymond Hettinger | dce583e | 2012-03-16 22:12:20 -0700 | [diff] [blame] | 146 | _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 147 |  | 
| Raymond Hettinger | 0c9050c | 2012-06-04 00:21:14 -0700 | [diff] [blame] | 148 | class _HashedSeq(list): | 
| Raymond Hettinger | 9acbb60 | 2012-04-30 22:32:16 -0700 | [diff] [blame] | 149 | __slots__ = 'hashvalue' | 
|  | 150 |  | 
| Raymond Hettinger | 0c9050c | 2012-06-04 00:21:14 -0700 | [diff] [blame] | 151 | def __init__(self, tup, hash=hash): | 
|  | 152 | self[:] = tup | 
|  | 153 | self.hashvalue = hash(tup) | 
| Raymond Hettinger | 9acbb60 | 2012-04-30 22:32:16 -0700 | [diff] [blame] | 154 |  | 
|  | 155 | def __hash__(self): | 
|  | 156 | return self.hashvalue | 
|  | 157 |  | 
| Raymond Hettinger | 0c9050c | 2012-06-04 00:21:14 -0700 | [diff] [blame] | 158 | def _make_key(args, kwds, typed, | 
|  | 159 | kwd_mark = (object(),), | 
|  | 160 | fasttypes = {int, str, frozenset, type(None)}, | 
|  | 161 | sorted=sorted, tuple=tuple, type=type, len=len): | 
|  | 162 | 'Make a cache key from optionally typed positional and keyword arguments' | 
|  | 163 | key = args | 
|  | 164 | if kwds: | 
|  | 165 | sorted_items = sorted(kwds.items()) | 
|  | 166 | key += kwd_mark | 
|  | 167 | for item in sorted_items: | 
|  | 168 | key += item | 
|  | 169 | if typed: | 
|  | 170 | key += tuple(type(v) for v in args) | 
|  | 171 | if kwds: | 
|  | 172 | key += tuple(type(v) for k, v in sorted_items) | 
|  | 173 | elif len(key) == 1 and type(key[0]) in fasttypes: | 
|  | 174 | return key[0] | 
|  | 175 | return _HashedSeq(key) | 
|  | 176 |  | 
| Raymond Hettinger | 010ce32 | 2012-05-19 21:20:48 -0700 | [diff] [blame] | 177 | def lru_cache(maxsize=128, typed=False): | 
| Benjamin Peterson | 1f594ad | 2010-08-08 13:17:07 +0000 | [diff] [blame] | 178 | """Least-recently-used cache decorator. | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 179 |  | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 180 | If *maxsize* is set to None, the LRU features are disabled and the cache | 
|  | 181 | can grow without bound. | 
|  | 182 |  | 
| Raymond Hettinger | cd9fdfd | 2011-10-20 08:57:45 -0700 | [diff] [blame] | 183 | If *typed* is True, arguments of different types will be cached separately. | 
|  | 184 | For example, f(3.0) and f(3) will be treated as distinct calls with | 
|  | 185 | distinct results. | 
|  | 186 |  | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 187 | Arguments to the cached function must be hashable. | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 188 |  | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 189 | View the cache statistics named tuple (hits, misses, maxsize, currsize) | 
|  | 190 | with f.cache_info().  Clear the cache and statistics with f.cache_clear(). | 
| Raymond Hettinger | 00f2f97 | 2010-12-01 00:47:56 +0000 | [diff] [blame] | 191 | Access the underlying function with f.__wrapped__. | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 192 |  | 
|  | 193 | See:  http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 194 |  | 
| Benjamin Peterson | 1f594ad | 2010-08-08 13:17:07 +0000 | [diff] [blame] | 195 | """ | 
| Raymond Hettinger | 1ff50df | 2012-03-30 13:15:48 -0700 | [diff] [blame] | 196 |  | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 197 | # Users should only access the lru_cache through its public API: | 
| Raymond Hettinger | 5e20bab | 2010-11-30 07:13:04 +0000 | [diff] [blame] | 198 | #       cache_info, cache_clear, and f.__wrapped__ | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 199 | # The internals of the lru_cache are encapsulated for thread safety and | 
|  | 200 | # to allow the implementation to change (including a possible C version). | 
|  | 201 |  | 
| Raymond Hettinger | 9f0ab9f | 2012-04-29 14:55:27 -0700 | [diff] [blame] | 202 | # Constants shared by all lru cache instances: | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 203 | sentinel = object()          # unique object used to signal cache misses | 
| Raymond Hettinger | 0c9050c | 2012-06-04 00:21:14 -0700 | [diff] [blame] | 204 | make_key = _make_key         # build a key from the function arguments | 
| Raymond Hettinger | 9f0ab9f | 2012-04-29 14:55:27 -0700 | [diff] [blame] | 205 | PREV, NEXT, KEY, RESULT = 0, 1, 2, 3   # names for the link fields | 
|  | 206 |  | 
| Raymond Hettinger | 6e8c817 | 2012-03-16 16:53:05 -0700 | [diff] [blame] | 207 | def decorating_function(user_function): | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 208 |  | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 209 | cache = {} | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 210 | hits = misses = currsize = 0 | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 211 | full = False | 
| Raymond Hettinger | c689785 | 2012-03-31 02:19:06 -0700 | [diff] [blame] | 212 | cache_get = cache.get    # bound method to lookup a key or return None | 
| Raymond Hettinger | fd54117 | 2013-03-01 03:47:57 -0800 | [diff] [blame] | 213 | lock = RLock()           # because linkedlist updates aren't threadsafe | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 214 | root = []                # root of the circular doubly linked list | 
|  | 215 | root[:] = [root, root, None, None]     # initialize by pointing to self | 
| Raymond Hettinger | 6e8c817 | 2012-03-16 16:53:05 -0700 | [diff] [blame] | 216 |  | 
| Raymond Hettinger | 7e0c581 | 2012-03-17 15:10:24 -0700 | [diff] [blame] | 217 | if maxsize == 0: | 
|  | 218 |  | 
| Raymond Hettinger | 7e0c581 | 2012-03-17 15:10:24 -0700 | [diff] [blame] | 219 | def wrapper(*args, **kwds): | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 220 | # no caching, just a statistics update after a successful call | 
| Raymond Hettinger | 7e0c581 | 2012-03-17 15:10:24 -0700 | [diff] [blame] | 221 | nonlocal misses | 
| Raymond Hettinger | 7dabfed | 2012-03-17 15:11:09 -0700 | [diff] [blame] | 222 | result = user_function(*args, **kwds) | 
| Raymond Hettinger | 7e0c581 | 2012-03-17 15:10:24 -0700 | [diff] [blame] | 223 | misses += 1 | 
|  | 224 | return result | 
|  | 225 |  | 
|  | 226 | elif maxsize is None: | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 227 |  | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 228 | def wrapper(*args, **kwds): | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 229 | # simple caching without ordering or size limit | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 230 | nonlocal hits, misses, currsize | 
| Raymond Hettinger | 9acbb60 | 2012-04-30 22:32:16 -0700 | [diff] [blame] | 231 | key = make_key(args, kwds, typed) | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 232 | result = cache_get(key, sentinel) | 
|  | 233 | if result is not sentinel: | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 234 | hits += 1 | 
| Raymond Hettinger | 4b779b3 | 2011-10-15 23:50:42 -0700 | [diff] [blame] | 235 | return result | 
| Raymond Hettinger | 4b779b3 | 2011-10-15 23:50:42 -0700 | [diff] [blame] | 236 | result = user_function(*args, **kwds) | 
|  | 237 | cache[key] = result | 
|  | 238 | misses += 1 | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 239 | currsize += 1 | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 240 | return result | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 241 |  | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 242 | else: | 
| Raymond Hettinger | bc8e81d | 2012-03-17 00:24:09 -0700 | [diff] [blame] | 243 |  | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 244 | def wrapper(*args, **kwds): | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 245 | # size limited caching that tracks accesses by recency | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 246 | nonlocal root, hits, misses, currsize, full | 
| Raymond Hettinger | 9acbb60 | 2012-04-30 22:32:16 -0700 | [diff] [blame] | 247 | key = make_key(args, kwds, typed) | 
| Raymond Hettinger | 4b779b3 | 2011-10-15 23:50:42 -0700 | [diff] [blame] | 248 | with lock: | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 249 | link = cache_get(key) | 
|  | 250 | if link is not None: | 
| Raymond Hettinger | 7f7a5a7 | 2012-03-30 21:50:40 -0700 | [diff] [blame] | 251 | # move the link to the front of the circular queue | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 252 | link_prev, link_next, key, result = link | 
|  | 253 | link_prev[NEXT] = link_next | 
|  | 254 | link_next[PREV] = link_prev | 
|  | 255 | last = root[PREV] | 
|  | 256 | last[NEXT] = root[PREV] = link | 
|  | 257 | link[PREV] = last | 
|  | 258 | link[NEXT] = root | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 259 | hits += 1 | 
| Raymond Hettinger | 4b779b3 | 2011-10-15 23:50:42 -0700 | [diff] [blame] | 260 | return result | 
| Raymond Hettinger | 4b779b3 | 2011-10-15 23:50:42 -0700 | [diff] [blame] | 261 | result = user_function(*args, **kwds) | 
|  | 262 | with lock: | 
| Raymond Hettinger | 34d94a2 | 2012-04-30 14:14:28 -0700 | [diff] [blame] | 263 | if key in cache: | 
|  | 264 | # getting here means that this same key was added to the | 
|  | 265 | # cache while the lock was released.  since the link | 
|  | 266 | # update is already done, we need only return the | 
|  | 267 | # computed result and update the count of misses. | 
|  | 268 | pass | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 269 | elif full: | 
| Raymond Hettinger | 41eb79a | 2012-03-30 19:15:18 -0700 | [diff] [blame] | 270 | # use root to store the new key and result | 
|  | 271 | root[KEY] = key | 
|  | 272 | root[RESULT] = result | 
|  | 273 | cache[key] = root | 
|  | 274 | # empty the oldest link and make it the new root | 
|  | 275 | root = root[NEXT] | 
|  | 276 | del cache[root[KEY]] | 
| Raymond Hettinger | c689785 | 2012-03-31 02:19:06 -0700 | [diff] [blame] | 277 | root[KEY] = root[RESULT] = None | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 278 | else: | 
|  | 279 | # put result in a new link at the front of the queue | 
|  | 280 | last = root[PREV] | 
|  | 281 | link = [last, root, key, result] | 
|  | 282 | cache[key] = last[NEXT] = root[PREV] = link | 
|  | 283 | currsize += 1 | 
|  | 284 | full = (currsize == maxsize) | 
| Raymond Hettinger | ec0e910 | 2012-03-16 01:16:31 -0700 | [diff] [blame] | 285 | misses += 1 | 
| Raymond Hettinger | c79fb0e | 2010-12-01 03:45:41 +0000 | [diff] [blame] | 286 | return result | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 287 |  | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 288 | def cache_info(): | 
| Raymond Hettinger | 5e20bab | 2010-11-30 07:13:04 +0000 | [diff] [blame] | 289 | """Report cache statistics""" | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 290 | with lock: | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 291 | return _CacheInfo(hits, misses, maxsize, currsize) | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 292 |  | 
| Raymond Hettinger | 02566ec | 2010-09-04 22:46:06 +0000 | [diff] [blame] | 293 | def cache_clear(): | 
| Benjamin Peterson | 1f594ad | 2010-08-08 13:17:07 +0000 | [diff] [blame] | 294 | """Clear the cache and cache statistics""" | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 295 | nonlocal hits, misses, currsize, full | 
| Raymond Hettinger | cbe8813 | 2010-08-14 22:22:10 +0000 | [diff] [blame] | 296 | with lock: | 
|  | 297 | cache.clear() | 
| Benjamin Peterson | 954cf57 | 2012-03-16 18:22:26 -0500 | [diff] [blame] | 298 | root[:] = [root, root, None, None] | 
| Raymond Hettinger | b6b98c0 | 2012-04-29 18:09:02 -0700 | [diff] [blame] | 299 | hits = misses = currsize = 0 | 
| Raymond Hettinger | 018b4fb | 2012-04-30 20:48:55 -0700 | [diff] [blame] | 300 | full = False | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 301 |  | 
| Nick Coghlan | 234515a | 2010-11-30 06:19:46 +0000 | [diff] [blame] | 302 | wrapper.cache_info = cache_info | 
| Raymond Hettinger | 02566ec | 2010-09-04 22:46:06 +0000 | [diff] [blame] | 303 | wrapper.cache_clear = cache_clear | 
| Raymond Hettinger | 1ff50df | 2012-03-30 13:15:48 -0700 | [diff] [blame] | 304 | return update_wrapper(wrapper, user_function) | 
| Raymond Hettinger | 5fa40c0 | 2010-11-25 08:11:57 +0000 | [diff] [blame] | 305 |  | 
| Georg Brandl | 2e7346a | 2010-07-31 18:09:23 +0000 | [diff] [blame] | 306 | return decorating_function |