]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | """ |
2 | This is a minimal implementation of TTL-ed lru_cache function. | |
3 | ||
4 | Based on Python 3 functools and backports.functools_lru_cache. | |
5 | """ | |
6 | from __future__ import absolute_import | |
7 | ||
8 | from functools import wraps | |
9 | from collections import OrderedDict | |
10 | from threading import RLock | |
11 | from time import time | |
12 | ||
9f95a23c TL |
13 | try: |
14 | from typing import Tuple | |
15 | except ImportError: | |
16 | pass # For typing only | |
17 | ||
11fdf7f2 TL |
18 | |
19 | def ttl_cache(ttl, maxsize=128, typed=False): | |
20 | if typed is not False: | |
21 | raise NotImplementedError("typed caching not supported") | |
22 | ||
23 | def decorating_function(function): | |
9f95a23c | 24 | cache = OrderedDict() # type: OrderedDict[object, Tuple[bool, float]] |
11fdf7f2 TL |
25 | stats = [0, 0, 0] |
26 | rlock = RLock() | |
9f95a23c TL |
27 | setattr(function, 'cache_info', lambda: |
28 | "hits={}, misses={}, expired={}, maxsize={}, currsize={}".format( | |
29 | stats[0], stats[1], stats[2], maxsize, len(cache))) | |
11fdf7f2 TL |
30 | |
31 | @wraps(function) | |
32 | def wrapper(*args, **kwargs): | |
33 | key = args + tuple(kwargs.items()) | |
34 | with rlock: | |
35 | refresh = True | |
36 | if key in cache: | |
37 | (ret, ts) = cache[key] | |
38 | del cache[key] | |
39 | if time() - ts < ttl: | |
40 | refresh = False | |
41 | stats[0] += 1 | |
42 | else: | |
43 | stats[2] += 1 | |
44 | ||
45 | if refresh: | |
46 | ret = function(*args, **kwargs) | |
47 | ts = time() | |
48 | if len(cache) == maxsize: | |
49 | cache.popitem(last=False) | |
50 | stats[1] += 1 | |
51 | ||
52 | cache[key] = (ret, ts) | |
53 | ||
54 | return ret | |
55 | ||
56 | return wrapper | |
57 | return decorating_function |