Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1ba81ee

Browse files
committed
Closes #16613: Added optional mapping argument to ChainMap.new_child.
1 parent 569ff4f commit 1ba81ee

3 files changed

Lines changed: 49 additions & 7 deletions

File tree

Doc/library/collections.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,19 @@ The class can be used to simulate nested scopes and is useful in templating.
7676
be modified to change which mappings are searched. The list should
7777
always contain at least one mapping.
7878

79-
.. method:: new_child()
79+
.. method:: new_child(m=None)
8080

81-
Returns a new :class:`ChainMap` containing a new :class:`dict` followed by
82-
all of the maps in the current instance. A call to ``d.new_child()`` is
83-
equivalent to: ``ChainMap({}, *d.maps)``. This method is used for
81+
Returns a new :class:`ChainMap` containing a new map followed by
82+
all of the maps in the current instance. If ``m`` is specified,
83+
it becomes the new map at the front of the list of mappings; if not
84+
specified, an empty dict is used, so that a call to ``d.new_child()``
85+
is equivalent to: ``ChainMap({}, *d.maps)``. This method is used for
8486
creating subcontexts that can be updated without altering values in any
8587
of the parent mappings.
8688

89+
.. versionchanged:: 3.4
90+
The optional ``m`` parameter was added.
91+
8792
.. attribute:: parents
8893

8994
Property returning a new :class:`ChainMap` containing all of the maps in

Lib/collections/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,9 +821,14 @@ def copy(self):
821821

822822
__copy__ = copy
823823

824-
def new_child(self): # like Django's Context.push()
825-
'New ChainMap with a new dict followed by all previous maps.'
826-
return self.__class__({}, *self.maps)
824+
def new_child(self, m=None): # like Django's Context.push()
825+
'''
826+
New ChainMap with a new map followed by all previous maps. If no
827+
map is provided, an empty dict is used.
828+
'''
829+
if m is None:
830+
m = {}
831+
return self.__class__(m, *self.maps)
827832

828833
@property
829834
def parents(self): # like Django's Context.pop()

Lib/test/test_collections.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,38 @@ def test_dict_coercion(self):
112112
self.assertEqual(dict(d), dict(a=1, b=2, c=30))
113113
self.assertEqual(dict(d.items()), dict(a=1, b=2, c=30))
114114

115+
def test_new_child(self):
116+
'Tests for changes for issue #16613.'
117+
c = ChainMap()
118+
c['a'] = 1
119+
c['b'] = 2
120+
m = {'b':20, 'c': 30}
121+
d = c.new_child(m)
122+
self.assertEqual(d.maps, [{'b':20, 'c':30}, {'a':1, 'b':2}]) # check internal state
123+
self.assertIs(m, d.maps[0])
124+
125+
# Use a different map than a dict
126+
class lowerdict(dict):
127+
def __getitem__(self, key):
128+
if isinstance(key, str):
129+
key = key.lower()
130+
return dict.__getitem__(self, key)
131+
def __contains__(self, key):
132+
if isinstance(key, str):
133+
key = key.lower()
134+
return dict.__contains__(self, key)
135+
136+
c = ChainMap()
137+
c['a'] = 1
138+
c['b'] = 2
139+
m = lowerdict(b=20, c=30)
140+
d = c.new_child(m)
141+
self.assertIs(m, d.maps[0])
142+
for key in 'abc': # check contains
143+
self.assertIn(key, d)
144+
for k, v in dict(a=1, B=20, C=30, z=100).items(): # check get
145+
self.assertEqual(d.get(k, 100), v)
146+
115147

116148
################################################################################
117149
### Named Tuples

0 commit comments

Comments
 (0)