summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorINADA Naoki <methane@users.noreply.github.com>2017-09-30 07:13:02 (GMT)
committerGitHub <noreply@github.com>2017-09-30 07:13:02 (GMT)
commit9811e80fd0ed9d74c76a66f1dd4e4b8afa9e8f53 (patch)
treeca58c45f84820067c85f0487719de31b290cdc84
parentb24cd055ecb3eea9a15405a6ca72dafc739e6531 (diff)
downloadcpython-9811e80fd0ed9d74c76a66f1dd4e4b8afa9e8f53.zip
cpython-9811e80fd0ed9d74c76a66f1dd4e4b8afa9e8f53.tar.gz
cpython-9811e80fd0ed9d74c76a66f1dd4e4b8afa9e8f53.tar.bz2
bpo-31581: Reduce the number of imports for functools (GH-3757)
-rw-r--r--Lib/functools.py13
-rw-r--r--Lib/test/test_functools.py167
2 files changed, 92 insertions, 88 deletions
diff --git a/Lib/functools.py b/Lib/functools.py
index 25075de..a51dddf 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 f7a1166..68e94e7 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__':