summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCurtis Bucher <cpbucher5@gmail.com>2020-03-23 19:02:05 (GMT)
committerGitHub <noreply@github.com>2020-03-23 19:02:05 (GMT)
commitf393b2c588559162dc2e77f8079a42e48558870a (patch)
tree28bd1ee4ecf3104e76a7df28622aadc714400c6e
parent8ec7370c89aa522602eb9604086ce9f09770953d (diff)
downloadcpython-f393b2c588559162dc2e77f8079a42e48558870a.zip
cpython-f393b2c588559162dc2e77f8079a42e48558870a.tar.gz
cpython-f393b2c588559162dc2e77f8079a42e48558870a.tar.bz2
bpo-36144: Add PEP 584 operators to collections.ChainMap (#18832)
* Update ChainMap to include | and |= Created __ior__, __or__ and __ror__ methods in ChainMap class. * Update ACKS * Update docs * Update test_collections.py to include test_issue584(). Added testing for | and |= operators for ChainMap objects. * Update test_union_operators Renamed test_union operators, fixed errors and style problems raised by brandtbucher. * Update test_union_operators in TestChainMap Added testing for union operator between ChainMap and iterable of key-value pairs. * Update test_union operators in test_collections.py Gave more descriptive variable names and eliminated unnecessary tmp variable. * Update test_union_operators in test_collections.py Added cm3 * Check .maps rather than Chainmap equality. * Add news entry * Update Lib/test/test_collections.py Co-Authored-By: Brandt Bucher <brandtbucher@gmail.com> * Removed whitespace * Added Guido's changes * Fixed Docs * Removed whitespace Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
-rw-r--r--Doc/library/collections.rst3
-rw-r--r--Lib/collections/__init__.py19
-rw-r--r--Lib/test/test_collections.py45
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Library/2020-03-07-11-26-08.bpo-36144.FG9jqy.rst1
5 files changed, 69 insertions, 0 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 72d1052..c9533a3 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -116,6 +116,9 @@ The class can be used to simulate nested scopes and is useful in templating.
>>> list(combined)
['music', 'art', 'opera']
+ .. versionchanged:: 3.9
+ Added support for ``|`` and ``|=`` operators, specified in :pep:`584`.
+
.. seealso::
* The `MultiContext class
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index 18255da..e198406 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -979,6 +979,25 @@ class ChainMap(_collections_abc.MutableMapping):
'Clear maps[0], leaving maps[1:] intact.'
self.maps[0].clear()
+ def __ior__(self, other):
+ self.maps[0] |= other
+ return self
+
+ def __or__(self, other):
+ if isinstance(other, _collections_abc.Mapping):
+ m = self.maps[0].copy()
+ m.update(other)
+ return self.__class__(m, *self.maps[1:])
+ return NotImplemented
+
+ def __ror__(self, other):
+ if isinstance(other, _collections_abc.Mapping):
+ m = dict(other)
+ for child in reversed(self.maps):
+ m.update(child)
+ return self.__class__(m)
+ return NotImplemented
+
################################################################################
### UserDict
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 92520b0..47c500b 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -232,6 +232,51 @@ class TestChainMap(unittest.TestCase):
for k, v in dict(a=1, B=20, C=30, z=100).items(): # check get
self.assertEqual(d.get(k, 100), v)
+ def test_union_operators(self):
+ cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4))
+ cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4))
+ cm3 = cm1.copy()
+ d = dict(a=10, c=30)
+ pairs = [('c', 3), ('p',0)]
+
+ tmp = cm1 | cm2 # testing between chainmaps
+ self.assertEqual(tmp.maps, [cm1.maps[0] | dict(cm2), *cm1.maps[1:]])
+ cm1 |= cm2
+ self.assertEqual(tmp, cm1)
+
+ tmp = cm2 | d # testing between chainmap and mapping
+ self.assertEqual(tmp.maps, [cm2.maps[0] | d, *cm2.maps[1:]])
+ self.assertEqual((d | cm2).maps, [d | dict(cm2)])
+ cm2 |= d
+ self.assertEqual(tmp, cm2)
+
+ # testing behavior between chainmap and iterable key-value pairs
+ with self.assertRaises(TypeError):
+ cm3 | pairs
+ cm3 |= pairs
+ self.assertEqual(cm3.maps, [cm3.maps[0] | dict(pairs), *cm3.maps[1:]])
+
+ # testing proper return types for ChainMap and it's subclasses
+ class Subclass(ChainMap):
+ pass
+
+ class SubclassRor(ChainMap):
+ def __ror__(self, other):
+ return super().__ror__(other)
+
+ tmp = ChainMap() | ChainMap()
+ self.assertIs(type(tmp), ChainMap)
+ self.assertIs(type(tmp.maps[0]), dict)
+ tmp = ChainMap() | Subclass()
+ self.assertIs(type(tmp), ChainMap)
+ self.assertIs(type(tmp.maps[0]), dict)
+ tmp = Subclass() | ChainMap()
+ self.assertIs(type(tmp), Subclass)
+ self.assertIs(type(tmp.maps[0]), dict)
+ tmp = ChainMap() | SubclassRor()
+ self.assertIs(type(tmp), SubclassRor)
+ self.assertIs(type(tmp.maps[0]), dict)
+
################################################################################
### Named Tuples
diff --git a/Misc/ACKS b/Misc/ACKS
index 37cf7af..129db95 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -233,6 +233,7 @@ Floris Bruynooghe
Matt Bryant
Stan Bubrouski
Brandt Bucher
+Curtis Bucher
Colm Buckley
Erik de Bueger
Jan-Hein Bührman
diff --git a/Misc/NEWS.d/next/Library/2020-03-07-11-26-08.bpo-36144.FG9jqy.rst b/Misc/NEWS.d/next/Library/2020-03-07-11-26-08.bpo-36144.FG9jqy.rst
new file mode 100644
index 0000000..9deb489
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-03-07-11-26-08.bpo-36144.FG9jqy.rst
@@ -0,0 +1 @@
+Added :pep:`584` operators (``|`` and ``|=``) to :class:`collections.ChainMap`.