From 9811e80fd0ed9d74c76a66f1dd4e4b8afa9e8f53 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 30 Sep 2017 16:13:02 +0900 Subject: [PATCH] bpo-31581: Reduce the number of imports for functools (GH-3757) --- Lib/functools.py | 13 +-- Lib/test/test_functools.py | 167 +++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 88 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index 25075de5b40..a51dddf8785 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -19,8 +19,7 @@ except ImportError: pass from abc import get_cache_token from collections import namedtuple -from types import MappingProxyType -from weakref import WeakKeyDictionary +# import types, weakref # Deferred to single_dispatch() from reprlib import recursive_repr from _thread import RLock @@ -753,10 +752,14 @@ def singledispatch(func): function acts as the default implementation, and additional implementations can be registered using the register() attribute of the generic function. - """ + # There are many programs that use functools without singledispatch, so we + # trade-off making singledispatch marginally slower for the benefit of + # making start-up of such applications slightly faster. + import types, weakref + registry = {} - dispatch_cache = WeakKeyDictionary() + dispatch_cache = weakref.WeakKeyDictionary() cache_token = None def dispatch(cls): @@ -803,7 +806,7 @@ def singledispatch(func): registry[object] = func wrapper.register = register wrapper.dispatch = dispatch - wrapper.registry = MappingProxyType(registry) + wrapper.registry = types.MappingProxyType(registry) wrapper._clear_cache = dispatch_cache.clear update_wrapper(wrapper, func) return wrapper diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index f7a11666133..68e94e7ae17 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2019,6 +2019,8 @@ class TestSingleDispatch(unittest.TestCase): def test_cache_invalidation(self): from collections import UserDict + import weakref + class TracingDict(UserDict): def __init__(self, *args, **kwargs): super(TracingDict, self).__init__(*args, **kwargs) @@ -2033,90 +2035,89 @@ class TestSingleDispatch(unittest.TestCase): self.data[key] = value def clear(self): self.data.clear() - _orig_wkd = functools.WeakKeyDictionary + td = TracingDict() - functools.WeakKeyDictionary = lambda: td - c = collections.abc - @functools.singledispatch - def g(arg): - return "base" - d = {} - l = [] - self.assertEqual(len(td), 0) - self.assertEqual(g(d), "base") - self.assertEqual(len(td), 1) - self.assertEqual(td.get_ops, []) - self.assertEqual(td.set_ops, [dict]) - self.assertEqual(td.data[dict], g.registry[object]) - self.assertEqual(g(l), "base") - self.assertEqual(len(td), 2) - self.assertEqual(td.get_ops, []) - self.assertEqual(td.set_ops, [dict, list]) - self.assertEqual(td.data[dict], g.registry[object]) - self.assertEqual(td.data[list], g.registry[object]) - self.assertEqual(td.data[dict], td.data[list]) - self.assertEqual(g(l), "base") - self.assertEqual(g(d), "base") - self.assertEqual(td.get_ops, [list, dict]) - self.assertEqual(td.set_ops, [dict, list]) - g.register(list, lambda arg: "list") - self.assertEqual(td.get_ops, [list, dict]) - self.assertEqual(len(td), 0) - self.assertEqual(g(d), "base") - self.assertEqual(len(td), 1) - self.assertEqual(td.get_ops, [list, dict]) - self.assertEqual(td.set_ops, [dict, list, dict]) - self.assertEqual(td.data[dict], - functools._find_impl(dict, g.registry)) - self.assertEqual(g(l), "list") - self.assertEqual(len(td), 2) - self.assertEqual(td.get_ops, [list, dict]) - self.assertEqual(td.set_ops, [dict, list, dict, list]) - self.assertEqual(td.data[list], - functools._find_impl(list, g.registry)) - class X: - pass - c.MutableMapping.register(X) # Will not invalidate the cache, - # not using ABCs yet. - self.assertEqual(g(d), "base") - self.assertEqual(g(l), "list") - self.assertEqual(td.get_ops, [list, dict, dict, list]) - self.assertEqual(td.set_ops, [dict, list, dict, list]) - g.register(c.Sized, lambda arg: "sized") - self.assertEqual(len(td), 0) - self.assertEqual(g(d), "sized") - self.assertEqual(len(td), 1) - self.assertEqual(td.get_ops, [list, dict, dict, list]) - self.assertEqual(td.set_ops, [dict, list, dict, list, dict]) - self.assertEqual(g(l), "list") - self.assertEqual(len(td), 2) - self.assertEqual(td.get_ops, [list, dict, dict, list]) - self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) - self.assertEqual(g(l), "list") - self.assertEqual(g(d), "sized") - self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict]) - self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) - g.dispatch(list) - g.dispatch(dict) - self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict, - list, dict]) - self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) - c.MutableSet.register(X) # Will invalidate the cache. - self.assertEqual(len(td), 2) # Stale cache. - self.assertEqual(g(l), "list") - self.assertEqual(len(td), 1) - g.register(c.MutableMapping, lambda arg: "mutablemapping") - self.assertEqual(len(td), 0) - self.assertEqual(g(d), "mutablemapping") - self.assertEqual(len(td), 1) - self.assertEqual(g(l), "list") - self.assertEqual(len(td), 2) - g.register(dict, lambda arg: "dict") - self.assertEqual(g(d), "dict") - self.assertEqual(g(l), "list") - g._clear_cache() - self.assertEqual(len(td), 0) - functools.WeakKeyDictionary = _orig_wkd + with support.swap_attr(weakref, "WeakKeyDictionary", lambda: td): + c = collections.abc + @functools.singledispatch + def g(arg): + return "base" + d = {} + l = [] + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "base") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, []) + self.assertEqual(td.set_ops, [dict]) + self.assertEqual(td.data[dict], g.registry[object]) + self.assertEqual(g(l), "base") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, []) + self.assertEqual(td.set_ops, [dict, list]) + self.assertEqual(td.data[dict], g.registry[object]) + self.assertEqual(td.data[list], g.registry[object]) + self.assertEqual(td.data[dict], td.data[list]) + self.assertEqual(g(l), "base") + self.assertEqual(g(d), "base") + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list]) + g.register(list, lambda arg: "list") + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "base") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict]) + self.assertEqual(td.data[dict], + functools._find_impl(dict, g.registry)) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, [list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list]) + self.assertEqual(td.data[list], + functools._find_impl(list, g.registry)) + class X: + pass + c.MutableMapping.register(X) # Will not invalidate the cache, + # not using ABCs yet. + self.assertEqual(g(d), "base") + self.assertEqual(g(l), "list") + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list]) + g.register(c.Sized, lambda arg: "sized") + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "sized") + self.assertEqual(len(td), 1) + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict]) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + self.assertEqual(td.get_ops, [list, dict, dict, list]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + self.assertEqual(g(l), "list") + self.assertEqual(g(d), "sized") + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + g.dispatch(list) + g.dispatch(dict) + self.assertEqual(td.get_ops, [list, dict, dict, list, list, dict, + list, dict]) + self.assertEqual(td.set_ops, [dict, list, dict, list, dict, list]) + c.MutableSet.register(X) # Will invalidate the cache. + self.assertEqual(len(td), 2) # Stale cache. + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 1) + g.register(c.MutableMapping, lambda arg: "mutablemapping") + self.assertEqual(len(td), 0) + self.assertEqual(g(d), "mutablemapping") + self.assertEqual(len(td), 1) + self.assertEqual(g(l), "list") + self.assertEqual(len(td), 2) + g.register(dict, lambda arg: "dict") + self.assertEqual(g(d), "dict") + self.assertEqual(g(l), "list") + g._clear_cache() + self.assertEqual(len(td), 0) if __name__ == '__main__':