From d648fc8c8e3d8e95cae45d2dc3d274bc48ac7a02 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 26 Sep 2017 12:52:35 +0900 Subject: [PATCH 1/7] Reduce the number of imports for functools --- Lib/collections/__init__.py | 4 ++-- Lib/functools.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 50cf8141731183..d1ea97550b3f00 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -26,7 +26,6 @@ from operator import itemgetter as _itemgetter, eq as _eq from keyword import iskeyword as _iskeyword import sys as _sys -import heapq as _heapq from _weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap from reprlib import recursive_repr as _recursive_repr @@ -557,7 +556,8 @@ def most_common(self, n=None): # Emulate Bag.sortedByCount from Smalltalk if n is None: return sorted(self.items(), key=_itemgetter(1), reverse=True) - return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) + import heapq + return heapq.nlargest(n, self.items(), key=_itemgetter(1)) def elements(self): '''Iterator over elements repeating each as many times as its count. diff --git a/Lib/functools.py b/Lib/functools.py index 25075de5b40259..39fe27a29791f9 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -19,8 +19,6 @@ pass from abc import get_cache_token from collections import namedtuple -from types import MappingProxyType -from weakref import WeakKeyDictionary from reprlib import recursive_repr from _thread import RLock @@ -753,8 +751,10 @@ def singledispatch(func): function acts as the default implementation, and additional implementations can be registered using the register() attribute of the generic function. - """ + from types import MappingProxyType + from weakref import WeakKeyDictionary + registry = {} dispatch_cache = WeakKeyDictionary() cache_token = None From f228a84a19a62d6d3e4f13504f47fa15a4d717bf Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 26 Sep 2017 17:47:15 +0900 Subject: [PATCH 2/7] Revert collections module --- Lib/collections/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index d1ea97550b3f00..50cf8141731183 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -26,6 +26,7 @@ from operator import itemgetter as _itemgetter, eq as _eq from keyword import iskeyword as _iskeyword import sys as _sys +import heapq as _heapq from _weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap from reprlib import recursive_repr as _recursive_repr @@ -556,8 +557,7 @@ def most_common(self, n=None): # Emulate Bag.sortedByCount from Smalltalk if n is None: return sorted(self.items(), key=_itemgetter(1), reverse=True) - import heapq - return heapq.nlargest(n, self.items(), key=_itemgetter(1)) + return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) def elements(self): '''Iterator over elements repeating each as many times as its count. From cac0c738cf7f45db4b43cea2afb431afad0dec6b Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 26 Sep 2017 17:51:31 +0900 Subject: [PATCH 3/7] Add comment about lazy import --- Lib/functools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/functools.py b/Lib/functools.py index 39fe27a29791f9..e889fdaf8d01e2 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -752,6 +752,9 @@ def singledispatch(func): 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. from types import MappingProxyType from weakref import WeakKeyDictionary From 865f333c57569af1cc7aef657bc8ca46ba747dd4 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 26 Sep 2017 18:58:55 +0900 Subject: [PATCH 4/7] Fix TestSingleDispatch --- Lib/test/test_functools.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index f7a11666133d42..cd325061548fa7 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2019,6 +2019,8 @@ def _(arg): 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,9 +2035,9 @@ def __setitem__(self, key, value): self.data[key] = value def clear(self): self.data.clear() - _orig_wkd = functools.WeakKeyDictionary + _orig_wkd = weakref.WeakKeyDictionary td = TracingDict() - functools.WeakKeyDictionary = lambda: td + weakref.WeakKeyDictionary = lambda: td c = collections.abc @functools.singledispatch def g(arg): @@ -2116,7 +2118,7 @@ class X: self.assertEqual(g(l), "list") g._clear_cache() self.assertEqual(len(td), 0) - functools.WeakKeyDictionary = _orig_wkd + weakref.WeakKeyDictionary = _orig_wkd if __name__ == '__main__': From fd86dde4ed41749f79daeec82e9d1bf48bfbaf42 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 27 Sep 2017 18:16:11 +0900 Subject: [PATCH 5/7] Use import instead of from ... import --- Lib/functools.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index e889fdaf8d01e2..61814d092d4204 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -755,11 +755,10 @@ def singledispatch(func): # 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. - from types import MappingProxyType - from weakref import WeakKeyDictionary + import types, weakref registry = {} - dispatch_cache = WeakKeyDictionary() + dispatch_cache = weakref.WeakKeyDictionary() cache_token = None def dispatch(cls): @@ -806,7 +805,7 @@ def wrapper(*args, **kw): 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 From 26b0b326bff9f7a32e574dd08e4aa9fd6f9f450e Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 27 Sep 2017 18:20:09 +0900 Subject: [PATCH 6/7] Use support.swap_attr in TestSingleDispatch --- Lib/test/test_functools.py | 165 ++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 83 deletions(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index cd325061548fa7..68e94e7ae17430 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2035,90 +2035,89 @@ def __setitem__(self, key, value): self.data[key] = value def clear(self): self.data.clear() - _orig_wkd = weakref.WeakKeyDictionary + td = TracingDict() - 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) - weakref.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__': From 99eeeb9ea3a31d057d74bf80a7a83ef976153f5c Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 30 Sep 2017 15:56:15 +0900 Subject: [PATCH 7/7] Add import comment at top of the module. ref: https://bugs.python.org/issue31581#msg303366 --- Lib/functools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/functools.py b/Lib/functools.py index 61814d092d4204..a51dddf878584b 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -19,6 +19,7 @@ pass from abc import get_cache_token from collections import namedtuple +# import types, weakref # Deferred to single_dispatch() from reprlib import recursive_repr from _thread import RLock