[Python-checkins] bpo-36144: Add PEP 584 operators to collections.ChainMap (#18832)

Curtis Bucher webhook-mailer at python.org
Mon Mar 23 15:02:13 EDT 2020


https://github.com/python/cpython/commit/f393b2c588559162dc2e77f8079a42e48558870a
commit: f393b2c588559162dc2e77f8079a42e48558870a
branch: master
author: Curtis Bucher <cpbucher5 at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-03-23T12:02:05-07:00
summary:

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 at gmail.com>

* Removed whitespace

* Added Guido's changes

* Fixed Docs

* Removed whitespace

Co-authored-by: Brandt Bucher <brandtbucher at gmail.com>

files:
A Misc/NEWS.d/next/Library/2020-03-07-11-26-08.bpo-36144.FG9jqy.rst
M Doc/library/collections.rst
M Lib/collections/__init__.py
M Lib/test/test_collections.py
M Misc/ACKS

diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 72d1052064a03..c9533a3cb8f48 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 18255da17591c..e19840650dd7e 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -979,6 +979,25 @@ def clear(self):
         '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 92520b09bb8f2..47c500bcdd002 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -232,6 +232,51 @@ def __contains__(self, key):
         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 37cf7af0c2ec5..129db952d885f 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 0000000000000..9deb489d88352
--- /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`.



More information about the Python-checkins mailing list