Skip to content

Commit 9811e80

Browse files
authored
bpo-31581: Reduce the number of imports for functools (GH-3757)
1 parent b24cd05 commit 9811e80

File tree

2 files changed

+92
-88
lines changed

2 files changed

+92
-88
lines changed

Lib/functools.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
pass
2020
from abc import get_cache_token
2121
from collections import namedtuple
22-
from types import MappingProxyType
23-
from weakref import WeakKeyDictionary
22+
# import types, weakref # Deferred to single_dispatch()
2423
from reprlib import recursive_repr
2524
from _thread import RLock
2625

@@ -753,10 +752,14 @@ def singledispatch(func):
753752
function acts as the default implementation, and additional
754753
implementations can be registered using the register() attribute of the
755754
generic function.
756-
757755
"""
756+
# There are many programs that use functools without singledispatch, so we
757+
# trade-off making singledispatch marginally slower for the benefit of
758+
# making start-up of such applications slightly faster.
759+
import types, weakref
760+
758761
registry = {}
759-
dispatch_cache = WeakKeyDictionary()
762+
dispatch_cache = weakref.WeakKeyDictionary()
760763
cache_token = None
761764

762765
def dispatch(cls):
@@ -803,7 +806,7 @@ def wrapper(*args, **kw):
803806
registry[object] = func
804807
wrapper.register = register
805808
wrapper.dispatch = dispatch
806-
wrapper.registry = MappingProxyType(registry)
809+
wrapper.registry = types.MappingProxyType(registry)
807810
wrapper._clear_cache = dispatch_cache.clear
808811
update_wrapper(wrapper, func)
809812
return wrapper

Lib/test/test_functools.py

Lines changed: 84 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,8 @@ def _(arg):
20192019

20202020
def test_cache_invalidation(self):
20212021
from collections import UserDict
2022+
import weakref
2023+
20222024
class TracingDict(UserDict):
20232025
def __init__(self, *args, **kwargs):
20242026
super(TracingDict, self).__init__(*args, **kwargs)
@@ -2033,90 +2035,89 @@ def __setitem__(self, key, value):
20332035
self.data[key] = value
20342036
def clear(self):
20352037
self.data.clear()
2036-
_orig_wkd = functools.WeakKeyDictionary
2038+
20372039
td = TracingDict()
2038-
functools.WeakKeyDictionary = lambda: td
2039-
c = collections.abc
2040-
@functools.singledispatch
2041-
def g(arg):
2042-
return "base"
2043-
d = {}
2044-
l = []
2045-
self.assertEqual(len(td), 0)
2046-
self.assertEqual(g(d), "base")
2047-
self.assertEqual(len(td), 1)
2048-
self.assertEqual(td.get_ops, [])
2049-
self.assertEqual(td.set_ops, [dict])
2050-
self.assertEqual(td.data[dict], g.registry[object])
2051-
self.assertEqual(g(l), "base")
2052-
self.assertEqual(len(td), 2)
2053-
self.assertEqual(td.get_ops, [])
2054-
self.assertEqual(td.set_ops, [dict, list])
2055-
self.assertEqual(td.data[dict], g.registry[object])
2056-
self.assertEqual(td.data[list], g.registry[object])
2057-
self.assertEqual(td.data[dict], td.data[list])
2058-
self.assertEqual(g(l), "base")
2059-
self.assertEqual(g(d), "base")
2060-
self.assertEqual(td.get_ops, [list, dict])
2061-
self.assertEqual(td.set_ops, [dict, list])
2062-
g.register(list, lambda arg: "list")
2063-
self.assertEqual(td.get_ops, [list, dict])
2064-
self.assertEqual(len(td), 0)
2065-
self.assertEqual(g(d), "base")
2066-
self.assertEqual(len(td), 1)
2067-
self.assertEqual(td.get_ops, [list, dict])
2068-
self.assertEqual(td.set_ops, [dict, list, dict])
2069-
self.assertEqual(td.data[dict],
2070-
functools._find_impl(dict, g.registry))
2071-
self.assertEqual(g(l), "list")
2072-
self.assertEqual(len(td), 2)
2073-
self.assertEqual(td.get_ops, [list, dict])
2074-
self.assertEqual(td.set_ops, [dict, list, dict, list])
2075-
self.assertEqual(td.data[list],
2076-
functools._find_impl(list, g.registry))
2077-
class X:
2078-
pass
2079-
c.MutableMapping.register(X) # Will not invalidate the cache,
2080-
# not using ABCs yet.
2081-
self.assertEqual(g(d), "base")
2082-
self.assertEqual(g(l), "list")
2083-
self.assertEqual(td.get_ops, [list, dict, dict, list])
2084-
self.assertEqual(td.set_ops, [dict, list, dict, list])
2085-
g.register(c.Sized, lambda arg: "sized")
2086-
self.assertEqual(len(td), 0)
2087-
self.assertEqual(g(d), "sized")
2088-
self.assertEqual(len(td), 1)
2089-
self.assertEqual(td.get_ops, [list, dict, dict, list])
2090-
self.assertEqual(td.set_ops, [dict, list, dict, list, dict])
2091-
self.assertEqual(g(l), "list")
2092-
self.assertEqual(len(td), 2)
2093-
self.assertEqual(td.get_ops, [list, dict, dict, list])
2094-
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2095-
self.assertEqual(g(l), "list")
2096-
self.assertEqual(g(d), "sized")
2097-
self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict])
2098-
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2099-
g.dispatch(list)
2100-
g.dispatch(dict)
2101-
self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict,
2102-
list, dict])
2103-
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2104-
c.MutableSet.register(X) # Will invalidate the cache.
2105-
self.assertEqual(len(td), 2) # Stale cache.
2106-
self.assertEqual(g(l), "list")
2107-
self.assertEqual(len(td), 1)
2108-
g.register(c.MutableMapping, lambda arg: "mutablemapping")
2109-
self.assertEqual(len(td), 0)
2110-
self.assertEqual(g(d), "mutablemapping")
2111-
self.assertEqual(len(td), 1)
2112-
self.assertEqual(g(l), "list")
2113-
self.assertEqual(len(td), 2)
2114-
g.register(dict, lambda arg: "dict")
2115-
self.assertEqual(g(d), "dict")
2116-
self.assertEqual(g(l), "list")
2117-
g._clear_cache()
2118-
self.assertEqual(len(td), 0)
2119-
functools.WeakKeyDictionary = _orig_wkd
2040+
with support.swap_attr(weakref, "WeakKeyDictionary", lambda: td):
2041+
c = collections.abc
2042+
@functools.singledispatch
2043+
def g(arg):
2044+
return "base"
2045+
d = {}
2046+
l = []
2047+
self.assertEqual(len(td), 0)
2048+
self.assertEqual(g(d), "base")
2049+
self.assertEqual(len(td), 1)
2050+
self.assertEqual(td.get_ops, [])
2051+
self.assertEqual(td.set_ops, [dict])
2052+
self.assertEqual(td.data[dict], g.registry[object])
2053+
self.assertEqual(g(l), "base")
2054+
self.assertEqual(len(td), 2)
2055+
self.assertEqual(td.get_ops, [])
2056+
self.assertEqual(td.set_ops, [dict, list])
2057+
self.assertEqual(td.data[dict], g.registry[object])
2058+
self.assertEqual(td.data[list], g.registry[object])
2059+
self.assertEqual(td.data[dict], td.data[list])
2060+
self.assertEqual(g(l), "base")
2061+
self.assertEqual(g(d), "base")
2062+
self.assertEqual(td.get_ops, [list, dict])
2063+
self.assertEqual(td.set_ops, [dict, list])
2064+
g.register(list, lambda arg: "list")
2065+
self.assertEqual(td.get_ops, [list, dict])
2066+
self.assertEqual(len(td), 0)
2067+
self.assertEqual(g(d), "base")
2068+
self.assertEqual(len(td), 1)
2069+
self.assertEqual(td.get_ops, [list, dict])
2070+
self.assertEqual(td.set_ops, [dict, list, dict])
2071+
self.assertEqual(td.data[dict],
2072+
functools._find_impl(dict, g.registry))
2073+
self.assertEqual(g(l), "list")
2074+
self.assertEqual(len(td), 2)
2075+
self.assertEqual(td.get_ops, [list, dict])
2076+
self.assertEqual(td.set_ops, [dict, list, dict, list])
2077+
self.assertEqual(td.data[list],
2078+
functools._find_impl(list, g.registry))
2079+
class X:
2080+
pass
2081+
c.MutableMapping.register(X) # Will not invalidate the cache,
2082+
# not using ABCs yet.
2083+
self.assertEqual(g(d), "base")
2084+
self.assertEqual(g(l), "list")
2085+
self.assertEqual(td.get_ops, [list, dict, dict, list])
2086+
self.assertEqual(td.set_ops, [dict, list, dict, list])
2087+
g.register(c.Sized, lambda arg: "sized")
2088+
self.assertEqual(len(td), 0)
2089+
self.assertEqual(g(d), "sized")
2090+
self.assertEqual(len(td), 1)
2091+
self.assertEqual(td.get_ops, [list, dict, dict, list])
2092+
self.assertEqual(td.set_ops, [dict, list, dict, list, dict])
2093+
self.assertEqual(g(l), "list")
2094+
self.assertEqual(len(td), 2)
2095+
self.assertEqual(td.get_ops, [list, dict, dict, list])
2096+
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2097+
self.assertEqual(g(l), "list")
2098+
self.assertEqual(g(d), "sized")
2099+
self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict])
2100+
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2101+
g.dispatch(list)
2102+
g.dispatch(dict)
2103+
self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict,
2104+
list, dict])
2105+
self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list])
2106+
c.MutableSet.register(X) # Will invalidate the cache.
2107+
self.assertEqual(len(td), 2) # Stale cache.
2108+
self.assertEqual(g(l), "list")
2109+
self.assertEqual(len(td), 1)
2110+
g.register(c.MutableMapping, lambda arg: "mutablemapping")
2111+
self.assertEqual(len(td), 0)
2112+
self.assertEqual(g(d), "mutablemapping")
2113+
self.assertEqual(len(td), 1)
2114+
self.assertEqual(g(l), "list")
2115+
self.assertEqual(len(td), 2)
2116+
g.register(dict, lambda arg: "dict")
2117+
self.assertEqual(g(d), "dict")
2118+
self.assertEqual(g(l), "list")
2119+
g._clear_cache()
2120+
self.assertEqual(len(td), 0)
21202121

21212122

21222123
if __name__ == '__main__':

0 commit comments

Comments
 (0)