diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2021-04-24 14:13:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-24 14:13:51 (GMT) |
commit | c6ca368867bd68d44f333df840aa85d425a51410 (patch) | |
tree | 74312cafb5adb28257beed73ffdcb3bbc285807c /Lib/importlib/_functools.py | |
parent | ce9a0643496ba802ea97a3da20eace3a1117ea48 (diff) | |
download | cpython-c6ca368867bd68d44f333df840aa85d425a51410.zip cpython-c6ca368867bd68d44f333df840aa85d425a51410.tar.gz cpython-c6ca368867bd68d44f333df840aa85d425a51410.tar.bz2 |
bpo-43780: Sync with importlib_metadata 3.10 (GH-25297)
* bpo-43780: Sync with importlib_metadata 3.10.
* Add blurb
* Apply changes from importlib_metadata 3.10.1.
Diffstat (limited to 'Lib/importlib/_functools.py')
-rw-r--r-- | Lib/importlib/_functools.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/Lib/importlib/_functools.py b/Lib/importlib/_functools.py new file mode 100644 index 0000000..73f50d0 --- /dev/null +++ b/Lib/importlib/_functools.py @@ -0,0 +1,85 @@ +import types +import functools + + +# from jaraco.functools 3.3 +def method_cache(method, cache_wrapper=None): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + cache_wrapper = cache_wrapper or functools.lru_cache() + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return wrapper |