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>
This commit is contained in:
Curtis Bucher 2020-03-23 12:02:05 -07:00 committed by GitHub
parent 8ec7370c89
commit f393b2c588
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -233,6 +233,7 @@ Floris Bruynooghe
Matt Bryant
Stan Bubrouski
Brandt Bucher
Curtis Bucher
Colm Buckley
Erik de Bueger
Jan-Hein Bührman

View File

@ -0,0 +1 @@
Added :pep:`584` operators (``|`` and ``|=``) to :class:`collections.ChainMap`.