Closes #16613: Added optional mapping argument to ChainMap.new_child.

This commit is contained in:
Vinay Sajip 2013-01-11 23:39:53 +00:00
parent 569ff4fbbc
commit 1ba81ee19a
3 changed files with 49 additions and 7 deletions

View 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

View File

@ -821,9 +821,14 @@ def copy(self):
__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()

View File

@ -112,6 +112,38 @@ def test_dict_coercion(self):
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