Skip to content

Commit c6ca368

Browse files
authored
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.
1 parent ce9a064 commit c6ca368

File tree

6 files changed

+275
-75
lines changed

6 files changed

+275
-75
lines changed

Lib/importlib/_collections.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import collections
2+
3+
4+
# from jaraco.collections 3.3
5+
class FreezableDefaultDict(collections.defaultdict):
6+
"""
7+
Often it is desirable to prevent the mutation of
8+
a default dict after its initial construction, such
9+
as to prevent mutation during iteration.
10+
11+
>>> dd = FreezableDefaultDict(list)
12+
>>> dd[0].append('1')
13+
>>> dd.freeze()
14+
>>> dd[1]
15+
[]
16+
>>> len(dd)
17+
1
18+
"""
19+
20+
def __missing__(self, key):
21+
return getattr(self, '_frozen', super().__missing__)(key)
22+
23+
def freeze(self):
24+
self._frozen = lambda key: self.default_factory()
25+
26+
27+
class Pair(collections.namedtuple('Pair', 'name value')):
28+
@classmethod
29+
def parse(cls, text):
30+
return cls(*map(str.strip, text.split("=", 1)))

Lib/importlib/_functools.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import types
2+
import functools
3+
4+
5+
# from jaraco.functools 3.3
6+
def method_cache(method, cache_wrapper=None):
7+
"""
8+
Wrap lru_cache to support storing the cache data in the object instances.
9+
10+
Abstracts the common paradigm where the method explicitly saves an
11+
underscore-prefixed protected property on first call and returns that
12+
subsequently.
13+
14+
>>> class MyClass:
15+
... calls = 0
16+
...
17+
... @method_cache
18+
... def method(self, value):
19+
... self.calls += 1
20+
... return value
21+
22+
>>> a = MyClass()
23+
>>> a.method(3)
24+
3
25+
>>> for x in range(75):
26+
... res = a.method(x)
27+
>>> a.calls
28+
75
29+
30+
Note that the apparent behavior will be exactly like that of lru_cache
31+
except that the cache is stored on each instance, so values in one
32+
instance will not flush values from another, and when an instance is
33+
deleted, so are the cached values for that instance.
34+
35+
>>> b = MyClass()
36+
>>> for x in range(35):
37+
... res = b.method(x)
38+
>>> b.calls
39+
35
40+
>>> a.method(0)
41+
0
42+
>>> a.calls
43+
75
44+
45+
Note that if method had been decorated with ``functools.lru_cache()``,
46+
a.calls would have been 76 (due to the cached value of 0 having been
47+
flushed by the 'b' instance).
48+
49+
Clear the cache with ``.cache_clear()``
50+
51+
>>> a.method.cache_clear()
52+
53+
Same for a method that hasn't yet been called.
54+
55+
>>> c = MyClass()
56+
>>> c.method.cache_clear()
57+
58+
Another cache wrapper may be supplied:
59+
60+
>>> cache = functools.lru_cache(maxsize=2)
61+
>>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
62+
>>> a = MyClass()
63+
>>> a.method2()
64+
3
65+
66+
Caution - do not subsequently wrap the method with another decorator, such
67+
as ``@property``, which changes the semantics of the function.
68+
69+
See also
70+
http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
71+
for another implementation and additional justification.
72+
"""
73+
cache_wrapper = cache_wrapper or functools.lru_cache()
74+
75+
def wrapper(self, *args, **kwargs):
76+
# it's the first call, replace the method with a cached, bound method
77+
bound_method = types.MethodType(method, self)
78+
cached_method = cache_wrapper(bound_method)
79+
setattr(self, method.__name__, cached_method)
80+
return cached_method(*args, **kwargs)
81+
82+
# Support cache clear even before cache has been created.
83+
wrapper.cache_clear = lambda: None
84+
85+
return wrapper

0 commit comments

Comments
 (0)