py2docfx 0.1.22.dev2258230__py3-none-any.whl → 0.1.22.dev2270449__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py2docfx/docfx_yaml/process_doctree.py +3 -23
- py2docfx/docfx_yaml/translator.py +6 -35
- py2docfx/docfx_yaml/type_mapping.py +102 -0
- py2docfx/venv/basevenv/Lib/site-packages/certifi/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +8 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +7 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/managed_identity.py +7 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/shared_cache.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/interactive.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/msal_managed_identity_client.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +8 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +7 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/managed_identity.py +7 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/shared_cache.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/cachetools/__init__.py +96 -122
- py2docfx/venv/venv1/Lib/site-packages/cachetools/{_decorators.py → _cached.py} +106 -13
- py2docfx/venv/venv1/Lib/site-packages/cachetools/_cachedmethod.py +128 -0
- py2docfx/venv/venv1/Lib/site-packages/cachetools/func.py +5 -25
- py2docfx/venv/venv1/Lib/site-packages/certifi/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/client_options.py +9 -2
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/general_helpers.py +36 -0
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/grpc_helpers.py +10 -7
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/grpc_helpers_async.py +8 -3
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/base.py +13 -7
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/rest.py +19 -12
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/rest_asyncio.py +21 -0
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_default.py +66 -12
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_default_async.py +16 -10
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_helpers.py +41 -0
- py2docfx/venv/venv1/Lib/site-packages/google/auth/compute_engine/credentials.py +67 -6
- py2docfx/venv/venv1/Lib/site-packages/google/auth/credentials.py +161 -18
- py2docfx/venv/venv1/Lib/site-packages/google/auth/environment_vars.py +4 -0
- py2docfx/venv/venv1/Lib/site-packages/google/auth/external_account.py +33 -10
- py2docfx/venv/venv1/Lib/site-packages/google/auth/external_account_authorized_user.py +24 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/identity_pool.py +25 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/impersonated_credentials.py +57 -9
- py2docfx/venv/venv1/Lib/site-packages/google/auth/pluggable.py +25 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/oauth2/_client.py +117 -0
- py2docfx/venv/venv1/Lib/site-packages/google/oauth2/service_account.py +39 -4
- {py2docfx-0.1.22.dev2258230.dist-info → py2docfx-0.1.22.dev2270449.dist-info}/METADATA +1 -1
- {py2docfx-0.1.22.dev2258230.dist-info → py2docfx-0.1.22.dev2270449.dist-info}/RECORD +47 -45
- {py2docfx-0.1.22.dev2258230.dist-info → py2docfx-0.1.22.dev2270449.dist-info}/WHEEL +0 -0
- {py2docfx-0.1.22.dev2258230.dist-info → py2docfx-0.1.22.dev2270449.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,6 @@ __all__ = (
|
|
5
5
|
"FIFOCache",
|
6
6
|
"LFUCache",
|
7
7
|
"LRUCache",
|
8
|
-
"MRUCache",
|
9
8
|
"RRCache",
|
10
9
|
"TLRUCache",
|
11
10
|
"TTLCache",
|
@@ -13,7 +12,7 @@ __all__ = (
|
|
13
12
|
"cachedmethod",
|
14
13
|
)
|
15
14
|
|
16
|
-
__version__ = "
|
15
|
+
__version__ = "6.2.0"
|
17
16
|
|
18
17
|
import collections
|
19
18
|
import collections.abc
|
@@ -23,7 +22,6 @@ import random
|
|
23
22
|
import time
|
24
23
|
|
25
24
|
from . import keys
|
26
|
-
from ._decorators import _cached_wrapper
|
27
25
|
|
28
26
|
|
29
27
|
class _DefaultSize:
|
@@ -172,32 +170,78 @@ class FIFOCache(Cache):
|
|
172
170
|
class LFUCache(Cache):
|
173
171
|
"""Least Frequently Used (LFU) cache implementation."""
|
174
172
|
|
173
|
+
class _Link:
|
174
|
+
__slots__ = ("count", "keys", "next", "prev")
|
175
|
+
|
176
|
+
def __init__(self, count):
|
177
|
+
self.count = count
|
178
|
+
self.keys = set()
|
179
|
+
|
180
|
+
def unlink(self):
|
181
|
+
next = self.next
|
182
|
+
prev = self.prev
|
183
|
+
prev.next = next
|
184
|
+
next.prev = prev
|
185
|
+
|
175
186
|
def __init__(self, maxsize, getsizeof=None):
|
176
187
|
Cache.__init__(self, maxsize, getsizeof)
|
177
|
-
self.
|
188
|
+
self.__root = root = LFUCache._Link(0) # sentinel
|
189
|
+
root.prev = root.next = root
|
190
|
+
self.__links = {}
|
178
191
|
|
179
192
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
180
193
|
value = cache_getitem(self, key)
|
181
194
|
if key in self: # __missing__ may not store item
|
182
|
-
self.
|
195
|
+
self.__touch(key)
|
183
196
|
return value
|
184
197
|
|
185
198
|
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
186
199
|
cache_setitem(self, key, value)
|
187
|
-
|
200
|
+
if key in self.__links:
|
201
|
+
return self.__touch(key)
|
202
|
+
root = self.__root
|
203
|
+
link = root.next
|
204
|
+
if link.count != 1:
|
205
|
+
link = LFUCache._Link(1)
|
206
|
+
link.next = root.next
|
207
|
+
root.next = link.next.prev = link
|
208
|
+
link.prev = root
|
209
|
+
link.keys.add(key)
|
210
|
+
self.__links[key] = link
|
188
211
|
|
189
212
|
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
190
213
|
cache_delitem(self, key)
|
191
|
-
|
214
|
+
link = self.__links.pop(key)
|
215
|
+
link.keys.remove(key)
|
216
|
+
if not link.keys:
|
217
|
+
link.unlink()
|
192
218
|
|
193
219
|
def popitem(self):
|
194
220
|
"""Remove and return the `(key, value)` pair least frequently used."""
|
195
|
-
|
196
|
-
|
197
|
-
|
221
|
+
root = self.__root
|
222
|
+
curr = root.next
|
223
|
+
if curr is root:
|
198
224
|
raise KeyError("%s is empty" % type(self).__name__) from None
|
199
|
-
|
200
|
-
|
225
|
+
key = next(iter(curr.keys)) # remove an arbitrary element
|
226
|
+
return (key, self.pop(key))
|
227
|
+
|
228
|
+
def __touch(self, key):
|
229
|
+
"""Increment use count"""
|
230
|
+
link = self.__links[key]
|
231
|
+
curr = link.next
|
232
|
+
if curr.count != link.count + 1:
|
233
|
+
if len(link.keys) == 1:
|
234
|
+
link.count += 1
|
235
|
+
return
|
236
|
+
curr = LFUCache._Link(link.count + 1)
|
237
|
+
curr.next = link.next
|
238
|
+
link.next = curr.next.prev = curr
|
239
|
+
curr.prev = link
|
240
|
+
curr.keys.add(key)
|
241
|
+
link.keys.remove(key)
|
242
|
+
if not link.keys:
|
243
|
+
link.unlink()
|
244
|
+
self.__links[key] = curr
|
201
245
|
|
202
246
|
|
203
247
|
class LRUCache(Cache):
|
@@ -210,12 +254,12 @@ class LRUCache(Cache):
|
|
210
254
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
211
255
|
value = cache_getitem(self, key)
|
212
256
|
if key in self: # __missing__ may not store item
|
213
|
-
self.
|
257
|
+
self.__touch(key)
|
214
258
|
return value
|
215
259
|
|
216
260
|
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
217
261
|
cache_setitem(self, key, value)
|
218
|
-
self.
|
262
|
+
self.__touch(key)
|
219
263
|
|
220
264
|
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
221
265
|
cache_delitem(self, key)
|
@@ -230,70 +274,47 @@ class LRUCache(Cache):
|
|
230
274
|
else:
|
231
275
|
return (key, self.pop(key))
|
232
276
|
|
233
|
-
def
|
277
|
+
def __touch(self, key):
|
278
|
+
"""Mark as recently used"""
|
234
279
|
try:
|
235
280
|
self.__order.move_to_end(key)
|
236
281
|
except KeyError:
|
237
282
|
self.__order[key] = None
|
238
283
|
|
239
284
|
|
240
|
-
class MRUCache(Cache):
|
241
|
-
"""Most Recently Used (MRU) cache implementation."""
|
242
|
-
|
243
|
-
def __init__(self, maxsize, getsizeof=None):
|
244
|
-
from warnings import warn
|
245
|
-
|
246
|
-
warn("MRUCache is deprecated", DeprecationWarning, stacklevel=2)
|
247
|
-
|
248
|
-
Cache.__init__(self, maxsize, getsizeof)
|
249
|
-
self.__order = collections.OrderedDict()
|
250
|
-
|
251
|
-
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
252
|
-
value = cache_getitem(self, key)
|
253
|
-
if key in self: # __missing__ may not store item
|
254
|
-
self.__update(key)
|
255
|
-
return value
|
256
|
-
|
257
|
-
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
258
|
-
cache_setitem(self, key, value)
|
259
|
-
self.__update(key)
|
260
|
-
|
261
|
-
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
262
|
-
cache_delitem(self, key)
|
263
|
-
del self.__order[key]
|
264
|
-
|
265
|
-
def popitem(self):
|
266
|
-
"""Remove and return the `(key, value)` pair most recently used."""
|
267
|
-
try:
|
268
|
-
key = next(iter(self.__order))
|
269
|
-
except StopIteration:
|
270
|
-
raise KeyError("%s is empty" % type(self).__name__) from None
|
271
|
-
else:
|
272
|
-
return (key, self.pop(key))
|
273
|
-
|
274
|
-
def __update(self, key):
|
275
|
-
try:
|
276
|
-
self.__order.move_to_end(key, last=False)
|
277
|
-
except KeyError:
|
278
|
-
self.__order[key] = None
|
279
|
-
|
280
|
-
|
281
285
|
class RRCache(Cache):
|
282
286
|
"""Random Replacement (RR) cache implementation."""
|
283
287
|
|
284
288
|
def __init__(self, maxsize, choice=random.choice, getsizeof=None):
|
285
289
|
Cache.__init__(self, maxsize, getsizeof)
|
286
290
|
self.__choice = choice
|
291
|
+
self.__index = {}
|
292
|
+
self.__keys = []
|
287
293
|
|
288
294
|
@property
|
289
295
|
def choice(self):
|
290
296
|
"""The `choice` function used by the cache."""
|
291
297
|
return self.__choice
|
292
298
|
|
299
|
+
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
300
|
+
cache_setitem(self, key, value)
|
301
|
+
if key not in self.__index:
|
302
|
+
self.__index[key] = len(self.__keys)
|
303
|
+
self.__keys.append(key)
|
304
|
+
|
305
|
+
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
306
|
+
cache_delitem(self, key)
|
307
|
+
index = self.__index.pop(key)
|
308
|
+
if index != len(self.__keys) - 1:
|
309
|
+
last = self.__keys[-1]
|
310
|
+
self.__keys[index] = last
|
311
|
+
self.__index[last] = index
|
312
|
+
self.__keys.pop()
|
313
|
+
|
293
314
|
def popitem(self):
|
294
315
|
"""Remove and return a random `(key, value)` pair."""
|
295
316
|
try:
|
296
|
-
key = self.__choice(
|
317
|
+
key = self.__choice(self.__keys)
|
297
318
|
except IndexError:
|
298
319
|
raise KeyError("%s is empty" % type(self).__name__) from None
|
299
320
|
else:
|
@@ -636,11 +657,23 @@ _CacheInfo = collections.namedtuple(
|
|
636
657
|
)
|
637
658
|
|
638
659
|
|
639
|
-
def cached(cache, key=keys.hashkey, lock=None, info=False):
|
660
|
+
def cached(cache, key=keys.hashkey, lock=None, condition=None, info=False):
|
640
661
|
"""Decorator to wrap a function with a memoizing callable that saves
|
641
662
|
results in a cache.
|
642
663
|
|
643
664
|
"""
|
665
|
+
from ._cached import _wrapper
|
666
|
+
|
667
|
+
if isinstance(condition, bool):
|
668
|
+
from warnings import warn
|
669
|
+
|
670
|
+
warn(
|
671
|
+
"passing `info` as positional parameter is deprecated",
|
672
|
+
DeprecationWarning,
|
673
|
+
stacklevel=2,
|
674
|
+
)
|
675
|
+
info = condition
|
676
|
+
condition = None
|
644
677
|
|
645
678
|
def decorator(func):
|
646
679
|
if info:
|
@@ -659,80 +692,21 @@ def cached(cache, key=keys.hashkey, lock=None, info=False):
|
|
659
692
|
def make_info(hits, misses):
|
660
693
|
return _CacheInfo(hits, misses, 0, 0)
|
661
694
|
|
662
|
-
|
695
|
+
return _wrapper(func, cache, key, lock, condition, info=make_info)
|
663
696
|
else:
|
664
|
-
|
665
|
-
|
666
|
-
wrapper.cache = cache
|
667
|
-
wrapper.cache_key = key
|
668
|
-
wrapper.cache_lock = lock
|
669
|
-
|
670
|
-
return functools.update_wrapper(wrapper, func)
|
697
|
+
return _wrapper(func, cache, key, lock, condition)
|
671
698
|
|
672
699
|
return decorator
|
673
700
|
|
674
701
|
|
675
|
-
def cachedmethod(cache, key=keys.methodkey, lock=None):
|
702
|
+
def cachedmethod(cache, key=keys.methodkey, lock=None, condition=None):
|
676
703
|
"""Decorator to wrap a class or instance method with a memoizing
|
677
704
|
callable that saves results in a cache.
|
678
705
|
|
679
706
|
"""
|
707
|
+
from ._cachedmethod import _wrapper
|
680
708
|
|
681
709
|
def decorator(method):
|
682
|
-
|
683
|
-
|
684
|
-
def wrapper(self, *args, **kwargs):
|
685
|
-
c = cache(self)
|
686
|
-
if c is None:
|
687
|
-
return method(self, *args, **kwargs)
|
688
|
-
k = key(self, *args, **kwargs)
|
689
|
-
try:
|
690
|
-
return c[k]
|
691
|
-
except KeyError:
|
692
|
-
pass # key not found
|
693
|
-
v = method(self, *args, **kwargs)
|
694
|
-
try:
|
695
|
-
c[k] = v
|
696
|
-
except ValueError:
|
697
|
-
pass # value too large
|
698
|
-
return v
|
699
|
-
|
700
|
-
def clear(self):
|
701
|
-
c = cache(self)
|
702
|
-
if c is not None:
|
703
|
-
c.clear()
|
704
|
-
|
705
|
-
else:
|
706
|
-
|
707
|
-
def wrapper(self, *args, **kwargs):
|
708
|
-
c = cache(self)
|
709
|
-
if c is None:
|
710
|
-
return method(self, *args, **kwargs)
|
711
|
-
k = key(self, *args, **kwargs)
|
712
|
-
try:
|
713
|
-
with lock(self):
|
714
|
-
return c[k]
|
715
|
-
except KeyError:
|
716
|
-
pass # key not found
|
717
|
-
v = method(self, *args, **kwargs)
|
718
|
-
# in case of a race, prefer the item already in the cache
|
719
|
-
try:
|
720
|
-
with lock(self):
|
721
|
-
return c.setdefault(k, v)
|
722
|
-
except ValueError:
|
723
|
-
return v # value too large
|
724
|
-
|
725
|
-
def clear(self):
|
726
|
-
c = cache(self)
|
727
|
-
if c is not None:
|
728
|
-
with lock(self):
|
729
|
-
c.clear()
|
730
|
-
|
731
|
-
wrapper.cache = cache
|
732
|
-
wrapper.cache_key = key
|
733
|
-
wrapper.cache_lock = lock
|
734
|
-
wrapper.cache_clear = clear
|
735
|
-
|
736
|
-
return functools.update_wrapper(wrapper, method)
|
710
|
+
return _wrapper(method, cache, key, lock, condition)
|
737
711
|
|
738
712
|
return decorator
|
@@ -1,7 +1,53 @@
|
|
1
|
-
"""
|
1
|
+
"""Function decorator helpers."""
|
2
2
|
|
3
|
+
import functools
|
3
4
|
|
4
|
-
|
5
|
+
|
6
|
+
def _condition_info(func, cache, key, lock, cond, info):
|
7
|
+
hits = misses = 0
|
8
|
+
pending = set()
|
9
|
+
|
10
|
+
def wrapper(*args, **kwargs):
|
11
|
+
nonlocal hits, misses
|
12
|
+
k = key(*args, **kwargs)
|
13
|
+
with lock:
|
14
|
+
cond.wait_for(lambda: k not in pending)
|
15
|
+
try:
|
16
|
+
result = cache[k]
|
17
|
+
hits += 1
|
18
|
+
return result
|
19
|
+
except KeyError:
|
20
|
+
pending.add(k)
|
21
|
+
misses += 1
|
22
|
+
try:
|
23
|
+
v = func(*args, **kwargs)
|
24
|
+
with lock:
|
25
|
+
try:
|
26
|
+
cache[k] = v
|
27
|
+
except ValueError:
|
28
|
+
pass # value too large
|
29
|
+
return v
|
30
|
+
finally:
|
31
|
+
with lock:
|
32
|
+
pending.remove(k)
|
33
|
+
cond.notify_all()
|
34
|
+
|
35
|
+
def cache_clear():
|
36
|
+
nonlocal hits, misses
|
37
|
+
with lock:
|
38
|
+
cache.clear()
|
39
|
+
hits = misses = 0
|
40
|
+
|
41
|
+
def cache_info():
|
42
|
+
with lock:
|
43
|
+
return info(hits, misses)
|
44
|
+
|
45
|
+
wrapper.cache_clear = cache_clear
|
46
|
+
wrapper.cache_info = cache_info
|
47
|
+
return wrapper
|
48
|
+
|
49
|
+
|
50
|
+
def _locked_info(func, cache, key, lock, info):
|
5
51
|
hits = misses = 0
|
6
52
|
|
7
53
|
def wrapper(*args, **kwargs):
|
@@ -37,7 +83,7 @@ def _cached_locked_info(func, cache, key, lock, info):
|
|
37
83
|
return wrapper
|
38
84
|
|
39
85
|
|
40
|
-
def
|
86
|
+
def _unlocked_info(func, cache, key, info):
|
41
87
|
hits = misses = 0
|
42
88
|
|
43
89
|
def wrapper(*args, **kwargs):
|
@@ -83,7 +129,40 @@ def _uncached_info(func, info):
|
|
83
129
|
return wrapper
|
84
130
|
|
85
131
|
|
86
|
-
def
|
132
|
+
def _condition(func, cache, key, lock, cond):
|
133
|
+
pending = set()
|
134
|
+
|
135
|
+
def wrapper(*args, **kwargs):
|
136
|
+
k = key(*args, **kwargs)
|
137
|
+
with lock:
|
138
|
+
cond.wait_for(lambda: k not in pending)
|
139
|
+
try:
|
140
|
+
result = cache[k]
|
141
|
+
return result
|
142
|
+
except KeyError:
|
143
|
+
pending.add(k)
|
144
|
+
try:
|
145
|
+
v = func(*args, **kwargs)
|
146
|
+
with lock:
|
147
|
+
try:
|
148
|
+
cache[k] = v
|
149
|
+
except ValueError:
|
150
|
+
pass # value too large
|
151
|
+
return v
|
152
|
+
finally:
|
153
|
+
with lock:
|
154
|
+
pending.remove(k)
|
155
|
+
cond.notify_all()
|
156
|
+
|
157
|
+
def cache_clear():
|
158
|
+
with lock:
|
159
|
+
cache.clear()
|
160
|
+
|
161
|
+
wrapper.cache_clear = cache_clear
|
162
|
+
return wrapper
|
163
|
+
|
164
|
+
|
165
|
+
def _locked(func, cache, key, lock):
|
87
166
|
def wrapper(*args, **kwargs):
|
88
167
|
k = key(*args, **kwargs)
|
89
168
|
with lock:
|
@@ -107,7 +186,7 @@ def _cached_locked(func, cache, key, lock):
|
|
107
186
|
return wrapper
|
108
187
|
|
109
188
|
|
110
|
-
def
|
189
|
+
def _unlocked(func, cache, key):
|
111
190
|
def wrapper(*args, **kwargs):
|
112
191
|
k = key(*args, **kwargs)
|
113
192
|
try:
|
@@ -133,20 +212,34 @@ def _uncached(func):
|
|
133
212
|
return wrapper
|
134
213
|
|
135
214
|
|
136
|
-
def
|
215
|
+
def _wrapper(func, cache, key, lock=None, cond=None, info=None):
|
137
216
|
if info is not None:
|
138
217
|
if cache is None:
|
139
218
|
wrapper = _uncached_info(func, info)
|
140
|
-
elif lock is None:
|
141
|
-
wrapper =
|
219
|
+
elif cond is not None and lock is not None:
|
220
|
+
wrapper = _condition_info(func, cache, key, lock, cond, info)
|
221
|
+
elif cond is not None:
|
222
|
+
wrapper = _condition_info(func, cache, key, cond, cond, info)
|
223
|
+
elif lock is not None:
|
224
|
+
wrapper = _locked_info(func, cache, key, lock, info)
|
142
225
|
else:
|
143
|
-
wrapper =
|
226
|
+
wrapper = _unlocked_info(func, cache, key, info)
|
144
227
|
else:
|
145
228
|
if cache is None:
|
146
229
|
wrapper = _uncached(func)
|
147
|
-
elif lock is None:
|
148
|
-
wrapper =
|
230
|
+
elif cond is not None and lock is not None:
|
231
|
+
wrapper = _condition(func, cache, key, lock, cond)
|
232
|
+
elif cond is not None:
|
233
|
+
wrapper = _condition(func, cache, key, cond, cond)
|
234
|
+
elif lock is not None:
|
235
|
+
wrapper = _locked(func, cache, key, lock)
|
149
236
|
else:
|
150
|
-
wrapper =
|
237
|
+
wrapper = _unlocked(func, cache, key)
|
151
238
|
wrapper.cache_info = None
|
152
|
-
|
239
|
+
|
240
|
+
wrapper.cache = cache
|
241
|
+
wrapper.cache_key = key
|
242
|
+
wrapper.cache_lock = lock if lock is not None else cond
|
243
|
+
wrapper.cache_condition = cond
|
244
|
+
|
245
|
+
return functools.update_wrapper(wrapper, func)
|
@@ -0,0 +1,128 @@
|
|
1
|
+
"""Method decorator helpers."""
|
2
|
+
|
3
|
+
import functools
|
4
|
+
import weakref
|
5
|
+
|
6
|
+
|
7
|
+
def warn_cache_none():
|
8
|
+
from warnings import warn
|
9
|
+
|
10
|
+
warn(
|
11
|
+
"returning `None` from `cache(self)` is deprecated",
|
12
|
+
DeprecationWarning,
|
13
|
+
stacklevel=3,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def _condition(method, cache, key, lock, cond):
|
18
|
+
pending = weakref.WeakKeyDictionary()
|
19
|
+
|
20
|
+
def wrapper(self, *args, **kwargs):
|
21
|
+
c = cache(self)
|
22
|
+
if c is None:
|
23
|
+
warn_cache_none()
|
24
|
+
return method(self, *args, **kwargs)
|
25
|
+
k = key(self, *args, **kwargs)
|
26
|
+
with lock(self):
|
27
|
+
p = pending.setdefault(self, set())
|
28
|
+
cond(self).wait_for(lambda: k not in p)
|
29
|
+
try:
|
30
|
+
return c[k]
|
31
|
+
except KeyError:
|
32
|
+
p.add(k)
|
33
|
+
try:
|
34
|
+
v = method(self, *args, **kwargs)
|
35
|
+
with lock(self):
|
36
|
+
try:
|
37
|
+
c[k] = v
|
38
|
+
except ValueError:
|
39
|
+
pass # value too large
|
40
|
+
return v
|
41
|
+
finally:
|
42
|
+
with lock(self):
|
43
|
+
pending[self].remove(k)
|
44
|
+
cond(self).notify_all()
|
45
|
+
|
46
|
+
def cache_clear(self):
|
47
|
+
c = cache(self)
|
48
|
+
if c is not None:
|
49
|
+
with lock(self):
|
50
|
+
c.clear()
|
51
|
+
|
52
|
+
wrapper.cache_clear = cache_clear
|
53
|
+
return wrapper
|
54
|
+
|
55
|
+
|
56
|
+
def _locked(method, cache, key, lock):
|
57
|
+
def wrapper(self, *args, **kwargs):
|
58
|
+
c = cache(self)
|
59
|
+
if c is None:
|
60
|
+
warn_cache_none()
|
61
|
+
return method(self, *args, **kwargs)
|
62
|
+
k = key(self, *args, **kwargs)
|
63
|
+
with lock(self):
|
64
|
+
try:
|
65
|
+
return c[k]
|
66
|
+
except KeyError:
|
67
|
+
pass # key not found
|
68
|
+
v = method(self, *args, **kwargs)
|
69
|
+
# in case of a race, prefer the item already in the cache
|
70
|
+
with lock(self):
|
71
|
+
try:
|
72
|
+
return c.setdefault(k, v)
|
73
|
+
except ValueError:
|
74
|
+
return v # value too large
|
75
|
+
|
76
|
+
def cache_clear(self):
|
77
|
+
c = cache(self)
|
78
|
+
if c is not None:
|
79
|
+
with lock(self):
|
80
|
+
c.clear()
|
81
|
+
|
82
|
+
wrapper.cache_clear = cache_clear
|
83
|
+
return wrapper
|
84
|
+
|
85
|
+
|
86
|
+
def _unlocked(method, cache, key):
|
87
|
+
def wrapper(self, *args, **kwargs):
|
88
|
+
c = cache(self)
|
89
|
+
if c is None:
|
90
|
+
warn_cache_none()
|
91
|
+
return method(self, *args, **kwargs)
|
92
|
+
k = key(self, *args, **kwargs)
|
93
|
+
try:
|
94
|
+
return c[k]
|
95
|
+
except KeyError:
|
96
|
+
pass # key not found
|
97
|
+
v = method(self, *args, **kwargs)
|
98
|
+
try:
|
99
|
+
c[k] = v
|
100
|
+
except ValueError:
|
101
|
+
pass # value too large
|
102
|
+
return v
|
103
|
+
|
104
|
+
def cache_clear(self):
|
105
|
+
c = cache(self)
|
106
|
+
if c is not None:
|
107
|
+
c.clear()
|
108
|
+
|
109
|
+
wrapper.cache_clear = cache_clear
|
110
|
+
return wrapper
|
111
|
+
|
112
|
+
|
113
|
+
def _wrapper(method, cache, key, lock=None, cond=None):
|
114
|
+
if cond is not None and lock is not None:
|
115
|
+
wrapper = _condition(method, cache, key, lock, cond)
|
116
|
+
elif cond is not None:
|
117
|
+
wrapper = _condition(method, cache, key, cond, cond)
|
118
|
+
elif lock is not None:
|
119
|
+
wrapper = _locked(method, cache, key, lock)
|
120
|
+
else:
|
121
|
+
wrapper = _unlocked(method, cache, key)
|
122
|
+
|
123
|
+
wrapper.cache = cache
|
124
|
+
wrapper.cache_key = key
|
125
|
+
wrapper.cache_lock = lock if lock is not None else cond
|
126
|
+
wrapper.cache_condition = cond
|
127
|
+
|
128
|
+
return functools.update_wrapper(wrapper, method)
|
@@ -1,17 +1,14 @@
|
|
1
1
|
"""`functools.lru_cache` compatible memoizing function decorators."""
|
2
2
|
|
3
|
-
__all__ = ("fifo_cache", "lfu_cache", "lru_cache", "
|
3
|
+
__all__ = ("fifo_cache", "lfu_cache", "lru_cache", "rr_cache", "ttl_cache")
|
4
4
|
|
5
|
+
import functools
|
5
6
|
import math
|
6
7
|
import random
|
7
8
|
import time
|
9
|
+
from threading import Condition
|
8
10
|
|
9
|
-
|
10
|
-
from threading import RLock
|
11
|
-
except ImportError: # pragma: no cover
|
12
|
-
from dummy_threading import RLock
|
13
|
-
|
14
|
-
from . import FIFOCache, LFUCache, LRUCache, MRUCache, RRCache, TTLCache
|
11
|
+
from . import FIFOCache, LFUCache, LRUCache, RRCache, TTLCache
|
15
12
|
from . import cached
|
16
13
|
from . import keys
|
17
14
|
|
@@ -28,7 +25,7 @@ class _UnboundTTLCache(TTLCache):
|
|
28
25
|
def _cache(cache, maxsize, typed):
|
29
26
|
def decorator(func):
|
30
27
|
key = keys.typedkey if typed else keys.hashkey
|
31
|
-
wrapper = cached(cache=cache, key=key,
|
28
|
+
wrapper = cached(cache=cache, key=key, condition=Condition(), info=True)(func)
|
32
29
|
wrapper.cache_parameters = lambda: {"maxsize": maxsize, "typed": typed}
|
33
30
|
return wrapper
|
34
31
|
|
@@ -77,23 +74,6 @@ def lru_cache(maxsize=128, typed=False):
|
|
77
74
|
return _cache(LRUCache(maxsize), maxsize, typed)
|
78
75
|
|
79
76
|
|
80
|
-
def mru_cache(maxsize=128, typed=False):
|
81
|
-
"""Decorator to wrap a function with a memoizing callable that saves
|
82
|
-
up to `maxsize` results based on a Most Recently Used (MRU)
|
83
|
-
algorithm.
|
84
|
-
"""
|
85
|
-
from warnings import warn
|
86
|
-
|
87
|
-
warn("@mru_cache is deprecated", DeprecationWarning, stacklevel=2)
|
88
|
-
|
89
|
-
if maxsize is None:
|
90
|
-
return _cache({}, None, typed)
|
91
|
-
elif callable(maxsize):
|
92
|
-
return _cache(MRUCache(128), 128, typed)(maxsize)
|
93
|
-
else:
|
94
|
-
return _cache(MRUCache(maxsize), maxsize, typed)
|
95
|
-
|
96
|
-
|
97
77
|
def rr_cache(maxsize=128, choice=random.choice, typed=False):
|
98
78
|
"""Decorator to wrap a function with a memoizing callable that saves
|
99
79
|
up to `maxsize` results based on a Random Replacement (RR)
|