From f353473f151a3c0fec62eaa1a001b408543098c5 Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Wed, 18 Mar 2020 13:41:19 -0700 Subject: [PATCH 1/8] Added Union operators to WeakKeyDictionary Added `_ior_`, `__or__`, and `__ror__` methods to WeakKeyDictionary and added corresponding tests to test_weakref.py. --- Lib/test/test_weakref.py | 15 +++++++++++++++ Lib/weakref.py | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 63c725527d5f25..82b28354d34a83 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1624,6 +1624,21 @@ def test_weak_keyed_delitem(self): self.assertEqual(len(d), 1) self.assertEqual(list(d.keys()), [o2]) + def test_weak_keyed_union_operators(self): + class C: pass + c1 = C() + c2 = C() + c3 = C() + + wvd1 = weakref.WeakKeyDictionary({c1: '1', c2: '2'}) + wvd2 = weakref.WeakKeyDictionary({c3: '3', c1: '4'}) + + wvd3 = wvd1 | wvd2 + self.assertEqual(dict(wvd3), dict(wvd1) | dict(wvd2)) + + wvd1 |= wvd2 + self.assertEqual(wvd1, wvd3) + def test_weak_valued_delitem(self): d = weakref.WeakValueDictionary() o1 = Object('1') diff --git a/Lib/weakref.py b/Lib/weakref.py index e3c2ce2d9b8b86..e6dd33027af089 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -488,6 +488,20 @@ def update(self, dict=None, /, **kwargs): if len(kwargs): self.update(kwargs) + def __ior__(self, other): + self.update(other) + return self + + def __or__(self, other): + c = self.copy() + c.update(other) + return c + + def __ror__(self, other): + c = other.copy() + c.update(self) + return c + class finalize: """Class for finalization of weakrefable objects From d7e7a2c634edf24bdb9e6b54061346f6c3e17ffc Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Wed, 18 Mar 2020 14:03:29 -0700 Subject: [PATCH 2/8] Updated docs and Added news entry --- Doc/library/weakref.rst | 3 +++ .../next/Library/2020-03-18-14-02-58.bpo-36144.ooyn6Z.rst | 1 + 2 files changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2020-03-18-14-02-58.bpo-36144.ooyn6Z.rst diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst index 8636e76c52a420..c10f436bea4c4e 100644 --- a/Doc/library/weakref.rst +++ b/Doc/library/weakref.rst @@ -171,6 +171,9 @@ Extension types can easily be made to support weak references; see performed by the program during iteration may cause items in the dictionary to vanish "by magic" (as a side effect of garbage collection). + .. versionchanged:: 3.9 + Added support for ``|`` and ``|=`` operators, specified in :pep:`584`. + :class:`WeakKeyDictionary` objects have an additional method that exposes the internal references directly. The references are not guaranteed to be "live" at the time they are used, so the result of calling the references diff --git a/Misc/NEWS.d/next/Library/2020-03-18-14-02-58.bpo-36144.ooyn6Z.rst b/Misc/NEWS.d/next/Library/2020-03-18-14-02-58.bpo-36144.ooyn6Z.rst new file mode 100644 index 00000000000000..262653a01b9235 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-18-14-02-58.bpo-36144.ooyn6Z.rst @@ -0,0 +1 @@ +Added :pep:`584` operators to :class:`weakref.WeakKeyDictionary`. From 0c0c95d4886343ad18518f9d610787a33cfb34d7 Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Wed, 18 Mar 2020 14:06:21 -0700 Subject: [PATCH 3/8] Removed whitespace --- Lib/test/test_weakref.py | 2 +- Lib/weakref.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 82b28354d34a83..8c5abfc6542fc5 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1629,7 +1629,7 @@ class C: pass c1 = C() c2 = C() c3 = C() - + wvd1 = weakref.WeakKeyDictionary({c1: '1', c2: '2'}) wvd2 = weakref.WeakKeyDictionary({c3: '3', c1: '4'}) diff --git a/Lib/weakref.py b/Lib/weakref.py index e6dd33027af089..b85afa1c928cc4 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -501,7 +501,7 @@ def __ror__(self, other): c = other.copy() c.update(self) return c - + class finalize: """Class for finalization of weakrefable objects From 08713a5a6400da816072493efbfaeab2bbe02db6 Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Wed, 18 Mar 2020 14:27:19 -0700 Subject: [PATCH 4/8] Update test_weakref.py --- Lib/test/test_weakref.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 8c5abfc6542fc5..31445e9d29579d 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1630,14 +1630,14 @@ class C: pass c2 = C() c3 = C() - wvd1 = weakref.WeakKeyDictionary({c1: '1', c2: '2'}) - wvd2 = weakref.WeakKeyDictionary({c3: '3', c1: '4'}) + wkd1 = weakref.WeakKeyDictionary({c1: '1', c2: '2'}) + wkd2 = weakref.WeakKeyDictionary({c3: '3', c1: '4'}) - wvd3 = wvd1 | wvd2 - self.assertEqual(dict(wvd3), dict(wvd1) | dict(wvd2)) + wkd3 = wkd1 | wkd2 + self.assertEqual(dict(wkd3), dict(wkd1) | dict(wkd2)) - wvd1 |= wvd2 - self.assertEqual(wvd1, wvd3) + wkd1 |= wkd2 + self.assertEqual(wkd1, wkd3) def test_weak_valued_delitem(self): d = weakref.WeakValueDictionary() From fcf1920ccb6db6d68eb18467fdbcf0e32dfeb22d Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Thu, 19 Mar 2020 13:18:20 -0700 Subject: [PATCH 5/8] Updated test_weakref.py and weakref.py --- Lib/test/test_weakref.py | 25 ++++++++++++++----------- Lib/weakref.py | 22 ++++++++++++++-------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 31445e9d29579d..4a84d36052d3b8 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1625,19 +1625,22 @@ def test_weak_keyed_delitem(self): self.assertEqual(list(d.keys()), [o2]) def test_weak_keyed_union_operators(self): - class C: pass - c1 = C() - c2 = C() - c3 = C() - - wkd1 = weakref.WeakKeyDictionary({c1: '1', c2: '2'}) - wkd2 = weakref.WeakKeyDictionary({c3: '3', c1: '4'}) - - wkd3 = wkd1 | wkd2 - self.assertEqual(dict(wkd3), dict(wkd1) | dict(wkd2)) + o1 = Object('1') + wkd1 = weakref.WeakKeyDictionary({o1: '1', C(): '2'}) + wkd2 = weakref.WeakKeyDictionary({C(): '3', o1: '4'}) + d1 = {C(): '5', o1: '6'} + tmp = wkd1 | wkd2 # Between two WeakKeyDictionaries + self.assertEqual(dict(tmp), dict(wkd1) | dict(wkd2)) + self.assertIsInstance(tmp, weakref.WeakKeyDictionary) wkd1 |= wkd2 - self.assertEqual(wkd1, wkd3) + self.assertEqual(wkd1, tmp) + + tmp = wkd2 | d1 # Between WeakKeyDictionary and mapping + self.assertEqual(dict(tmp), dict(wkd2) | d1) + self.assertIsInstance(tmp, weakref.WeakKeyDictionary) + wkd2 |= d1 + self.assertEqual(wkd2, tmp) def test_weak_valued_delitem(self): d = weakref.WeakValueDictionary() diff --git a/Lib/weakref.py b/Lib/weakref.py index b85afa1c928cc4..718e80a1edc276 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -489,18 +489,24 @@ def update(self, dict=None, /, **kwargs): self.update(kwargs) def __ior__(self, other): - self.update(other) - return self + if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): + self.update(other) + return self + return NotImplemented def __or__(self, other): - c = self.copy() - c.update(other) - return c + if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): + c = self.copy() + c.update(other) + return c + return NotImplemented def __ror__(self, other): - c = other.copy() - c.update(self) - return c + if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): + c = other.copy() + c.update(self) + return c + return NotImplemented class finalize: From c1ee9c62360adc3e5fd3139cd00f093f7fefc585 Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Thu, 19 Mar 2020 23:43:48 -0700 Subject: [PATCH 6/8] Update weakref.py Removed unecessary isinstance from __ior__ --- Lib/test/test_weakref.py | 8 ++++---- Lib/weakref.py | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 4a84d36052d3b8..258a3ea00e6df8 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1625,10 +1625,10 @@ def test_weak_keyed_delitem(self): self.assertEqual(list(d.keys()), [o2]) def test_weak_keyed_union_operators(self): - o1 = Object('1') - wkd1 = weakref.WeakKeyDictionary({o1: '1', C(): '2'}) - wkd2 = weakref.WeakKeyDictionary({C(): '3', o1: '4'}) - d1 = {C(): '5', o1: '6'} + o = Object('1') + wkd1 = weakref.WeakKeyDictionary({o: '1', C(): '2'}) + wkd2 = weakref.WeakKeyDictionary({C(): '3', o: '4'}) + d1 = {C(): '5', o: '6'} tmp = wkd1 | wkd2 # Between two WeakKeyDictionaries self.assertEqual(dict(tmp), dict(wkd1) | dict(wkd2)) diff --git a/Lib/weakref.py b/Lib/weakref.py index 718e80a1edc276..14229c03d65b3c 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -489,10 +489,8 @@ def update(self, dict=None, /, **kwargs): self.update(kwargs) def __ior__(self, other): - if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): - self.update(other) - return self - return NotImplemented + self.update(other) + return self def __or__(self, other): if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): From 3963090f50cf8322df3da500fe4e5cac0ed81503 Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Sat, 21 Mar 2020 15:44:13 -0700 Subject: [PATCH 7/8] Updated weakref.py and test_weakref.py Added tests and modified .__ror__ behavior --- Lib/test/test_weakref.py | 45 ++++++++++++++++++++++++++++------------ Lib/weakref.py | 3 ++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 258a3ea00e6df8..250ed40b20ca5c 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1625,22 +1625,41 @@ def test_weak_keyed_delitem(self): self.assertEqual(list(d.keys()), [o2]) def test_weak_keyed_union_operators(self): - o = Object('1') - wkd1 = weakref.WeakKeyDictionary({o: '1', C(): '2'}) - wkd2 = weakref.WeakKeyDictionary({C(): '3', o: '4'}) - d1 = {C(): '5', o: '6'} - - tmp = wkd1 | wkd2 # Between two WeakKeyDictionaries - self.assertEqual(dict(tmp), dict(wkd1) | dict(wkd2)) - self.assertIsInstance(tmp, weakref.WeakKeyDictionary) + o1 = C() + o2 = C() + o3 = C() + wkd1 = weakref.WeakKeyDictionary({o1: 1, o2: 2}) + wkd2 = weakref.WeakKeyDictionary({o3: 3, o1: 4}) + wkd3 = wkd1.copy() + d1 = {o2: '5', o3: '6'} + pairs = [(o2, 7), (o3, 8)] + + tmp1 = wkd1 | wkd2 # Between two WeakKeyDictionaries + self.assertEqual(dict(tmp1), dict(wkd1) | dict(wkd2)) + self.assertIs(type(tmp1), weakref.WeakKeyDictionary) wkd1 |= wkd2 - self.assertEqual(wkd1, tmp) + self.assertEqual(wkd1, tmp1) - tmp = wkd2 | d1 # Between WeakKeyDictionary and mapping - self.assertEqual(dict(tmp), dict(wkd2) | d1) - self.assertIsInstance(tmp, weakref.WeakKeyDictionary) + tmp2 = wkd2 | d1 # Between WeakKeyDictionary and mapping + self.assertEqual(dict(tmp2), dict(wkd2) | d1) + self.assertIs(type(tmp2), weakref.WeakKeyDictionary) wkd2 |= d1 - self.assertEqual(wkd2, tmp) + self.assertEqual(wkd2, tmp2) + + tmp3 = wkd3.copy() # Between WeakKeyDictionary and iterable key, value + tmp3 |= pairs + self.assertEqual(dict(tmp3), dict(wkd3) | dict(pairs)) + self.assertIs(type(tmp3), weakref.WeakKeyDictionary) + + tmp4 = d1 | wkd3 # Testing .__ror__ + self.assertEqual(dict(tmp4), d1 | dict(wkd3)) + self.assertIs(type(tmp4), weakref.WeakKeyDictionary) + + del o1 + self.assertNotIn(4, tmp1.values()) + self.assertNotIn(4, tmp2.values()) + self.assertNotIn(1, tmp3.values()) + self.assertNotIn(1, tmp4.values()) def test_weak_valued_delitem(self): d = weakref.WeakValueDictionary() diff --git a/Lib/weakref.py b/Lib/weakref.py index 14229c03d65b3c..d9da3db5fac48c 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -501,7 +501,8 @@ def __or__(self, other): def __ror__(self, other): if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): - c = other.copy() + c = self.__class__() + c.update(other) c.update(self) return c return NotImplemented From d2c340755d6d6381346836e8d5848897848a5d3b Mon Sep 17 00:00:00 2001 From: curtisbucher Date: Mon, 23 Mar 2020 13:24:42 -0700 Subject: [PATCH 8/8] Update Weakref.py Allow WeakKeyDictionary union with any mapping. --- Lib/weakref.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/weakref.py b/Lib/weakref.py index d9da3db5fac48c..759ad6dfa39b44 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -493,14 +493,14 @@ def __ior__(self, other): return self def __or__(self, other): - if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): + if isinstance(other, _collections_abc.Mapping): c = self.copy() c.update(other) return c return NotImplemented def __ror__(self, other): - if isinstance(other, (dict, WeakKeyDictionary, WeakValueDictionary)): + if isinstance(other, _collections_abc.Mapping): c = self.__class__() c.update(other) c.update(self)