-
Couldn't load subscription status.
- Fork 187
Add caching to more functions #198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8693d22
7e8cf5e
fc4b60d
ed02d18
5f83627
a84765d
44950b1
ca9ae7d
0e0dba1
21436d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,83 +1,61 @@ | ||
| import functools | ||
|
|
||
|
|
||
| class _HashedSeq(list): | ||
| # # From CPython | ||
| __slots__ = 'hashvalue' | ||
|
|
||
| def __init__(self, tup, hash=hash): | ||
| self[:] = tup | ||
| self.hashvalue = hash(tup) | ||
|
|
||
| def __hash__(self): | ||
| return self.hashvalue | ||
|
|
||
|
|
||
| # if we only have nonlocal | ||
| class _Cache(object): | ||
| def __init__(self): | ||
| self.off = False | ||
| self.missed = 0 | ||
| self.cache = {} | ||
|
|
||
|
|
||
| def function_cache(expected_max_entries=1000): | ||
| def function_cache(): | ||
| """ | ||
| function_cache is a decorator for caching function call | ||
| the argument to the wrapped function must be hashable else | ||
| function_cache is a decorator for caching function call. | ||
| The argument to the wrapped function must be hashable else | ||
| it will not work | ||
| expected_max_entries is for protecting cache failure. If cache | ||
| misses more than this number the cache will turn off itself. | ||
| Specify None you sure that the cache will not cause memory | ||
| limit problem. | ||
| Args: | ||
| expected_max_entries(integer OR None): will raise if not correct | ||
| Function wrapper accepts max_cache_entries argument which specifies | ||
| the maximum number of different data to cache. Specifying None will not | ||
| limit the size of the cache at all. | ||
| Returns: | ||
| function | ||
| """ | ||
| if ( | ||
| expected_max_entries is not None and | ||
| not isinstance(expected_max_entries, int) | ||
| ): | ||
| raise TypeError( | ||
| 'Expected expected_max_entries to be an integer or None' | ||
| ) | ||
|
|
||
| # indicator of cache missed | ||
| sentinel = object() | ||
|
|
||
| def decorator(func): | ||
| cached = _Cache() | ||
| cache = {} | ||
|
|
||
| @functools.wraps(func) | ||
| def inner(*args, **kwargs): | ||
| if cached.off: | ||
| max_cache_entries = kwargs.pop('max_cache_entries', 1000) | ||
|
|
||
| if ( | ||
| max_cache_entries is not None and | ||
| not isinstance(max_cache_entries, int) | ||
| ): | ||
| raise TypeError( | ||
| 'Expected max_cache_entries to be an integer or None' | ||
| ) | ||
|
|
||
| if max_cache_entries == 0: | ||
| return func(*args, **kwargs) | ||
|
|
||
| keys = args | ||
| keys = [] | ||
| for arg in args: | ||
| if isinstance(arg, list): | ||
| keys.append(tuple(arg)) | ||
| else: | ||
| keys.append(arg) | ||
|
|
||
| if kwargs: | ||
| sorted_items = sorted(kwargs.items()) | ||
| for item in sorted_items: | ||
| keys += item | ||
| keys.append(item) | ||
|
|
||
| hashed = hash(_HashedSeq(keys)) | ||
| result = cached.cache.get(hashed, sentinel) | ||
| hashed = hash(tuple(keys)) | ||
| result = cache.get(hashed, sentinel) | ||
| if result is sentinel: | ||
| cached.missed += 1 | ||
| result = func(*args, **kwargs) | ||
| cached.cache[hashed] = result | ||
| # # something is wrong if we are here more than expected | ||
| # # empty and turn it off | ||
| if ( | ||
| expected_max_entries is not None and | ||
| cached.missed > expected_max_entries | ||
| ): | ||
| cached.off = True | ||
| cached.cache.clear() | ||
|
|
||
| if max_cache_entries is None or len(cache) < max_cache_entries: | ||
| cache[hashed] = result | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit strange that the caching just decides to stubbornly refuse to add more entries. I've already forgotten what you said in the justification in the PR description. Can you please add a code comment here to explain this reasoning. |
||
|
|
||
| return result | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now is not the time but the correct name ought to be "function_memoize" or something like that.