comparison env/lib/python3.7/site-packages/glob2/compat.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 # Back-port functools.lru_cache to Python 2 (and <= 3.2)
2 # {{{ http://code.activestate.com/recipes/578078/ (r6)
3
4 from collections import namedtuple
5 from functools import update_wrapper
6 from threading import RLock
7
8 _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
9
10 class _HashedSeq(list):
11 __slots__ = 'hashvalue'
12
13 def __init__(self, tup, hash=hash):
14 self[:] = tup
15 self.hashvalue = hash(tup)
16
17 def __hash__(self):
18 return self.hashvalue
19
20 def _make_key(args, kwds, typed,
21 kwd_mark = (object(),),
22 fasttypes = set((int, str, frozenset, type(None))),
23 sorted=sorted, tuple=tuple, type=type, len=len):
24 'Make a cache key from optionally typed positional and keyword arguments'
25 key = args
26 if kwds:
27 sorted_items = sorted(kwds.items())
28 key += kwd_mark
29 for item in sorted_items:
30 key += item
31 if typed:
32 key += tuple(type(v) for v in args)
33 if kwds:
34 key += tuple(type(v) for k, v in sorted_items)
35 elif len(key) == 1 and type(key[0]) in fasttypes:
36 return key[0]
37 return _HashedSeq(key)
38
39 def lru_cache(maxsize=100, typed=False):
40 """Least-recently-used cache decorator.
41
42 If *maxsize* is set to None, the LRU features are disabled and the cache
43 can grow without bound.
44
45 If *typed* is True, arguments of different types will be cached separately.
46 For example, f(3.0) and f(3) will be treated as distinct calls with
47 distinct results.
48
49 Arguments to the cached function must be hashable.
50
51 View the cache statistics named tuple (hits, misses, maxsize, currsize) with
52 f.cache_info(). Clear the cache and statistics with f.cache_clear().
53 Access the underlying function with f.__wrapped__.
54
55 See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
56
57 """
58
59 # Users should only access the lru_cache through its public API:
60 # cache_info, cache_clear, and f.__wrapped__
61 # The internals of the lru_cache are encapsulated for thread safety and
62 # to allow the implementation to change (including a possible C version).
63
64 def decorating_function(user_function):
65
66 cache = dict()
67 stats = [0, 0] # make statistics updateable non-locally
68 HITS, MISSES = 0, 1 # names for the stats fields
69 make_key = _make_key
70 cache_get = cache.get # bound method to lookup key or return None
71 _len = len # localize the global len() function
72 lock = RLock() # because linkedlist updates aren't threadsafe
73 root = [] # root of the circular doubly linked list
74 root[:] = [root, root, None, None] # initialize by pointing to self
75 nonlocal_root = [root] # make updateable non-locally
76 PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
77
78 if maxsize == 0:
79
80 def wrapper(*args, **kwds):
81 # no caching, just do a statistics update after a successful call
82 result = user_function(*args, **kwds)
83 stats[MISSES] += 1
84 return result
85
86 elif maxsize is None:
87
88 def wrapper(*args, **kwds):
89 # simple caching without ordering or size limit
90 key = make_key(args, kwds, typed)
91 result = cache_get(key, root) # root used here as a unique not-found sentinel
92 if result is not root:
93 stats[HITS] += 1
94 return result
95 result = user_function(*args, **kwds)
96 cache[key] = result
97 stats[MISSES] += 1
98 return result
99
100 else:
101
102 def wrapper(*args, **kwds):
103 # size limited caching that tracks accesses by recency
104 key = make_key(args, kwds, typed) if kwds or typed else args
105 with lock:
106 link = cache_get(key)
107 if link is not None:
108 # record recent use of the key by moving it to the front of the list
109 root, = nonlocal_root
110 link_prev, link_next, key, result = link
111 link_prev[NEXT] = link_next
112 link_next[PREV] = link_prev
113 last = root[PREV]
114 last[NEXT] = root[PREV] = link
115 link[PREV] = last
116 link[NEXT] = root
117 stats[HITS] += 1
118 return result
119 result = user_function(*args, **kwds)
120 with lock:
121 root, = nonlocal_root
122 if key in cache:
123 # getting here means that this same key was added to the
124 # cache while the lock was released. since the link
125 # update is already done, we need only return the
126 # computed result and update the count of misses.
127 pass
128 elif _len(cache) >= maxsize:
129 # use the old root to store the new key and result
130 oldroot = root
131 oldroot[KEY] = key
132 oldroot[RESULT] = result
133 # empty the oldest link and make it the new root
134 root = nonlocal_root[0] = oldroot[NEXT]
135 oldkey = root[KEY]
136 oldvalue = root[RESULT]
137 root[KEY] = root[RESULT] = None
138 # now update the cache dictionary for the new links
139 del cache[oldkey]
140 cache[key] = oldroot
141 else:
142 # put result in a new link at the front of the list
143 last = root[PREV]
144 link = [last, root, key, result]
145 last[NEXT] = root[PREV] = cache[key] = link
146 stats[MISSES] += 1
147 return result
148
149 def cache_info():
150 """Report cache statistics"""
151 with lock:
152 return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache))
153
154 def cache_clear():
155 """Clear the cache and cache statistics"""
156 with lock:
157 cache.clear()
158 root = nonlocal_root[0]
159 root[:] = [root, root, None, None]
160 stats[:] = [0, 0]
161
162 wrapper.__wrapped__ = user_function
163 wrapper.cache_info = cache_info
164 wrapper.cache_clear = cache_clear
165 return update_wrapper(wrapper, user_function)
166
167 return decorating_function