]> granicus.if.org Git - python/commitdiff
Closes #16613: Added optional mapping argument to ChainMap.new_child.
authorVinay Sajip <vinay_sajip@yahoo.co.uk>
Fri, 11 Jan 2013 23:39:53 +0000 (23:39 +0000)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>
Fri, 11 Jan 2013 23:39:53 +0000 (23:39 +0000)
Doc/library/collections.rst
Lib/collections/__init__.py
Lib/test/test_collections.py

index 6ef114e677981ef84e2091293a38d32aaa77b6e1..ea4a31154f7378860283efb4c1f4ec0ef5ff0eea 100644 (file)
@@ -76,14 +76,19 @@ The class can be used to simulate nested scopes and is useful in templating.
         be modified to change which mappings are searched.  The list should
         always contain at least one mapping.
 
-    .. method:: new_child()
+    .. method:: new_child(m=None)
 
-        Returns a new :class:`ChainMap` containing a new :class:`dict` followed by
-        all of the maps in the current instance.  A call to ``d.new_child()`` is
-        equivalent to: ``ChainMap({}, *d.maps)``.  This method is used for
+        Returns a new :class:`ChainMap` containing a new map followed by
+        all of the maps in the current instance.  If ``m`` is specified,
+        it becomes the new map at the front of the list of mappings; if not
+        specified, an empty dict is used, so that a call to ``d.new_child()``
+        is equivalent to: ``ChainMap({}, *d.maps)``.  This method is used for
         creating subcontexts that can be updated without altering values in any
         of the parent mappings.
 
+        .. versionchanged:: 3.4
+           The optional ``m`` parameter was added.
+
     .. attribute:: parents
 
         Property returning a new :class:`ChainMap` containing all of the maps in
index 53083e4bd64adaabe57bf241cf7c4577c4e275a0..0612e1f61055496a9f6de5697ec37cae41c079c2 100644 (file)
@@ -821,9 +821,14 @@ class ChainMap(MutableMapping):
 
     __copy__ = copy
 
-    def new_child(self):                        # like Django's Context.push()
-        'New ChainMap with a new dict followed by all previous maps.'
-        return self.__class__({}, *self.maps)
+    def new_child(self, m=None):                # like Django's Context.push()
+        '''
+        New ChainMap with a new map followed by all previous maps. If no
+        map is provided, an empty dict is used.
+        '''
+        if m is None:
+            m = {}
+        return self.__class__(m, *self.maps)
 
     @property
     def parents(self):                          # like Django's Context.pop()
index 8850e8bc136893139ae200addbeb5f7a1783e722..dfad78e00566d6a5bcd144524f291835f7e469c8 100644 (file)
@@ -112,6 +112,38 @@ class TestChainMap(unittest.TestCase):
         self.assertEqual(dict(d), dict(a=1, b=2, c=30))
         self.assertEqual(dict(d.items()), dict(a=1, b=2, c=30))
 
+    def test_new_child(self):
+        'Tests for changes for issue #16613.'
+        c = ChainMap()
+        c['a'] = 1
+        c['b'] = 2
+        m = {'b':20, 'c': 30}
+        d = c.new_child(m)
+        self.assertEqual(d.maps, [{'b':20, 'c':30}, {'a':1, 'b':2}])  # check internal state
+        self.assertIs(m, d.maps[0])
+
+        # Use a different map than a dict
+        class lowerdict(dict):
+            def __getitem__(self, key):
+                if isinstance(key, str):
+                    key = key.lower()
+                return dict.__getitem__(self, key)
+            def __contains__(self, key):
+                if isinstance(key, str):
+                    key = key.lower()
+                return dict.__contains__(self, key)
+
+        c = ChainMap()
+        c['a'] = 1
+        c['b'] = 2
+        m = lowerdict(b=20, c=30)
+        d = c.new_child(m)
+        self.assertIs(m, d.maps[0])
+        for key in 'abc':                                             # check contains
+            self.assertIn(key, d)
+        for k, v in dict(a=1, B=20, C=30, z=100).items():             # check get
+            self.assertEqual(d.get(k, 100), v)
+
 
 ################################################################################
 ### Named Tuples