mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	Issue #22609: Constructors and update methods of mapping classes in the
collections module now accept the self keyword argument.
This commit is contained in:
		
						commit
						8943ecfab2
					
				
					 4 changed files with 121 additions and 28 deletions
				
			
		| 
						 | 
					@ -592,14 +592,15 @@ class MutableMapping(Mapping):
 | 
				
			||||||
            If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
 | 
					            If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
 | 
				
			||||||
            In either case, this is followed by: for k, v in F.items(): D[k] = v
 | 
					            In either case, this is followed by: for k, v in F.items(): D[k] = v
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        if len(args) > 2:
 | 
					        if not args:
 | 
				
			||||||
            raise TypeError("update() takes at most 2 positional "
 | 
					            raise TypeError("descriptor 'update' of 'MutableMapping' object "
 | 
				
			||||||
                            "arguments ({} given)".format(len(args)))
 | 
					                            "needs an argument")
 | 
				
			||||||
        elif not args:
 | 
					        self, *args = args
 | 
				
			||||||
            raise TypeError("update() takes at least 1 argument (0 given)")
 | 
					        if len(args) > 1:
 | 
				
			||||||
        self = args[0]
 | 
					            raise TypeError('update expected at most 1 arguments, got %d' %
 | 
				
			||||||
        other = args[1] if len(args) >= 2 else ()
 | 
					                            len(args))
 | 
				
			||||||
 | 
					        if args:
 | 
				
			||||||
 | 
					            other = args[0]
 | 
				
			||||||
            if isinstance(other, Mapping):
 | 
					            if isinstance(other, Mapping):
 | 
				
			||||||
                for key in other:
 | 
					                for key in other:
 | 
				
			||||||
                    self[key] = other[key]
 | 
					                    self[key] = other[key]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,12 +55,16 @@ class OrderedDict(dict):
 | 
				
			||||||
    # Individual links are kept alive by the hard reference in self.__map.
 | 
					    # Individual links are kept alive by the hard reference in self.__map.
 | 
				
			||||||
    # Those hard references disappear when a key is deleted from an OrderedDict.
 | 
					    # Those hard references disappear when a key is deleted from an OrderedDict.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, *args, **kwds):
 | 
					    def __init__(*args, **kwds):
 | 
				
			||||||
        '''Initialize an ordered dictionary.  The signature is the same as
 | 
					        '''Initialize an ordered dictionary.  The signature is the same as
 | 
				
			||||||
        regular dictionaries, but keyword arguments are not recommended because
 | 
					        regular dictionaries, but keyword arguments are not recommended because
 | 
				
			||||||
        their insertion order is arbitrary.
 | 
					        their insertion order is arbitrary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					        if not args:
 | 
				
			||||||
 | 
					            raise TypeError("descriptor '__init__' of 'OrderedDict' object "
 | 
				
			||||||
 | 
					                            "needs an argument")
 | 
				
			||||||
 | 
					        self, *args = args
 | 
				
			||||||
        if len(args) > 1:
 | 
					        if len(args) > 1:
 | 
				
			||||||
            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
					            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -479,7 +483,7 @@ class Counter(dict):
 | 
				
			||||||
    #   http://code.activestate.com/recipes/259174/
 | 
					    #   http://code.activestate.com/recipes/259174/
 | 
				
			||||||
    #   Knuth, TAOCP Vol. II section 4.6.3
 | 
					    #   Knuth, TAOCP Vol. II section 4.6.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, iterable=None, **kwds):
 | 
					    def __init__(*args, **kwds):
 | 
				
			||||||
        '''Create a new, empty Counter object.  And if given, count elements
 | 
					        '''Create a new, empty Counter object.  And if given, count elements
 | 
				
			||||||
        from an input iterable.  Or, initialize the count from another mapping
 | 
					        from an input iterable.  Or, initialize the count from another mapping
 | 
				
			||||||
        of elements to their counts.
 | 
					        of elements to their counts.
 | 
				
			||||||
| 
						 | 
					@ -490,8 +494,14 @@ class Counter(dict):
 | 
				
			||||||
        >>> c = Counter(a=4, b=2)                   # a new counter from keyword args
 | 
					        >>> c = Counter(a=4, b=2)                   # a new counter from keyword args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        super().__init__()
 | 
					        if not args:
 | 
				
			||||||
        self.update(iterable, **kwds)
 | 
					            raise TypeError("descriptor '__init__' of 'Counter' object "
 | 
				
			||||||
 | 
					                            "needs an argument")
 | 
				
			||||||
 | 
					        self, *args = args
 | 
				
			||||||
 | 
					        if len(args) > 1:
 | 
				
			||||||
 | 
					            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
				
			||||||
 | 
					        super(Counter, self).__init__()
 | 
				
			||||||
 | 
					        self.update(*args, **kwds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __missing__(self, key):
 | 
					    def __missing__(self, key):
 | 
				
			||||||
        'The count of elements not in the Counter is zero.'
 | 
					        'The count of elements not in the Counter is zero.'
 | 
				
			||||||
| 
						 | 
					@ -542,7 +552,7 @@ class Counter(dict):
 | 
				
			||||||
        raise NotImplementedError(
 | 
					        raise NotImplementedError(
 | 
				
			||||||
            'Counter.fromkeys() is undefined.  Use Counter(iterable) instead.')
 | 
					            'Counter.fromkeys() is undefined.  Use Counter(iterable) instead.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, iterable=None, **kwds):
 | 
					    def update(*args, **kwds):
 | 
				
			||||||
        '''Like dict.update() but add counts instead of replacing them.
 | 
					        '''Like dict.update() but add counts instead of replacing them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Source can be an iterable, a dictionary, or another Counter instance.
 | 
					        Source can be an iterable, a dictionary, or another Counter instance.
 | 
				
			||||||
| 
						 | 
					@ -562,6 +572,13 @@ class Counter(dict):
 | 
				
			||||||
        # contexts.  Instead, we implement straight-addition.  Both the inputs
 | 
					        # contexts.  Instead, we implement straight-addition.  Both the inputs
 | 
				
			||||||
        # and outputs are allowed to contain zero and negative counts.
 | 
					        # and outputs are allowed to contain zero and negative counts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not args:
 | 
				
			||||||
 | 
					            raise TypeError("descriptor 'update' of 'Counter' object "
 | 
				
			||||||
 | 
					                            "needs an argument")
 | 
				
			||||||
 | 
					        self, *args = args
 | 
				
			||||||
 | 
					        if len(args) > 1:
 | 
				
			||||||
 | 
					            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
				
			||||||
 | 
					        iterable = args[0] if args else None
 | 
				
			||||||
        if iterable is not None:
 | 
					        if iterable is not None:
 | 
				
			||||||
            if isinstance(iterable, Mapping):
 | 
					            if isinstance(iterable, Mapping):
 | 
				
			||||||
                if self:
 | 
					                if self:
 | 
				
			||||||
| 
						 | 
					@ -569,13 +586,13 @@ class Counter(dict):
 | 
				
			||||||
                    for elem, count in iterable.items():
 | 
					                    for elem, count in iterable.items():
 | 
				
			||||||
                        self[elem] = count + self_get(elem, 0)
 | 
					                        self[elem] = count + self_get(elem, 0)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    super().update(iterable) # fast path when counter is empty
 | 
					                    super(Counter, self).update(iterable) # fast path when counter is empty
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                _count_elements(self, iterable)
 | 
					                _count_elements(self, iterable)
 | 
				
			||||||
        if kwds:
 | 
					        if kwds:
 | 
				
			||||||
            self.update(kwds)
 | 
					            self.update(kwds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def subtract(self, iterable=None, **kwds):
 | 
					    def subtract(*args, **kwds):
 | 
				
			||||||
        '''Like dict.update() but subtracts counts instead of replacing them.
 | 
					        '''Like dict.update() but subtracts counts instead of replacing them.
 | 
				
			||||||
        Counts can be reduced below zero.  Both the inputs and outputs are
 | 
					        Counts can be reduced below zero.  Both the inputs and outputs are
 | 
				
			||||||
        allowed to contain zero and negative counts.
 | 
					        allowed to contain zero and negative counts.
 | 
				
			||||||
| 
						 | 
					@ -591,6 +608,13 @@ class Counter(dict):
 | 
				
			||||||
        -1
 | 
					        -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					        if not args:
 | 
				
			||||||
 | 
					            raise TypeError("descriptor 'subtract' of 'Counter' object "
 | 
				
			||||||
 | 
					                            "needs an argument")
 | 
				
			||||||
 | 
					        self, *args = args
 | 
				
			||||||
 | 
					        if len(args) > 1:
 | 
				
			||||||
 | 
					            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
				
			||||||
 | 
					        iterable = args[0] if args else None
 | 
				
			||||||
        if iterable is not None:
 | 
					        if iterable is not None:
 | 
				
			||||||
            self_get = self.get
 | 
					            self_get = self.get
 | 
				
			||||||
            if isinstance(iterable, Mapping):
 | 
					            if isinstance(iterable, Mapping):
 | 
				
			||||||
| 
						 | 
					@ -898,7 +922,14 @@ class ChainMap(MutableMapping):
 | 
				
			||||||
class UserDict(MutableMapping):
 | 
					class UserDict(MutableMapping):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Start by filling-out the abstract methods
 | 
					    # Start by filling-out the abstract methods
 | 
				
			||||||
    def __init__(self, dict=None, **kwargs):
 | 
					    def __init__(*args, **kwargs):
 | 
				
			||||||
 | 
					        if not args:
 | 
				
			||||||
 | 
					            raise TypeError("descriptor '__init__' of 'UserDict' object "
 | 
				
			||||||
 | 
					                            "needs an argument")
 | 
				
			||||||
 | 
					        self, *args = args
 | 
				
			||||||
 | 
					        if len(args) > 1:
 | 
				
			||||||
 | 
					            raise TypeError('expected at most 1 arguments, got %d' % len(args))
 | 
				
			||||||
 | 
					        dict = args[0] if args else None
 | 
				
			||||||
        self.data = {}
 | 
					        self.data = {}
 | 
				
			||||||
        if dict is not None:
 | 
					        if dict is not None:
 | 
				
			||||||
            self.update(dict)
 | 
					            self.update(dict)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1137,6 +1137,28 @@ class TestCounter(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual(c.setdefault('e', 5), 5)
 | 
					        self.assertEqual(c.setdefault('e', 5), 5)
 | 
				
			||||||
        self.assertEqual(c['e'], 5)
 | 
					        self.assertEqual(c['e'], 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_init(self):
 | 
				
			||||||
 | 
					        self.assertEqual(list(Counter(self=42).items()), [('self', 42)])
 | 
				
			||||||
 | 
					        self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)])
 | 
				
			||||||
 | 
					        self.assertEqual(list(Counter(iterable=None).items()), [('iterable', None)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter, (), ())
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter.__init__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_update(self):
 | 
				
			||||||
 | 
					        c = Counter()
 | 
				
			||||||
 | 
					        c.update(self=42)
 | 
				
			||||||
 | 
					        self.assertEqual(list(c.items()), [('self', 42)])
 | 
				
			||||||
 | 
					        c = Counter()
 | 
				
			||||||
 | 
					        c.update(iterable=42)
 | 
				
			||||||
 | 
					        self.assertEqual(list(c.items()), [('iterable', 42)])
 | 
				
			||||||
 | 
					        c = Counter()
 | 
				
			||||||
 | 
					        c.update(iterable=None)
 | 
				
			||||||
 | 
					        self.assertEqual(list(c.items()), [('iterable', None)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter().update, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter().update, {}, {})
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter.update)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_copying(self):
 | 
					    def test_copying(self):
 | 
				
			||||||
        # Check that counters are copyable, deepcopyable, picklable, and
 | 
					        # Check that counters are copyable, deepcopyable, picklable, and
 | 
				
			||||||
        #have a repr/eval round-trip
 | 
					        #have a repr/eval round-trip
 | 
				
			||||||
| 
						 | 
					@ -1258,6 +1280,16 @@ class TestCounter(unittest.TestCase):
 | 
				
			||||||
        c.subtract('aaaabbcce')
 | 
					        c.subtract('aaaabbcce')
 | 
				
			||||||
        self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
 | 
					        self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        c = Counter()
 | 
				
			||||||
 | 
					        c.subtract(self=42)
 | 
				
			||||||
 | 
					        self.assertEqual(list(c.items()), [('self', -42)])
 | 
				
			||||||
 | 
					        c = Counter()
 | 
				
			||||||
 | 
					        c.subtract(iterable=42)
 | 
				
			||||||
 | 
					        self.assertEqual(list(c.items()), [('iterable', -42)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter().subtract, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter().subtract, {}, {})
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, Counter.subtract)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_unary(self):
 | 
					    def test_unary(self):
 | 
				
			||||||
        c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
 | 
					        c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
 | 
				
			||||||
        self.assertEqual(dict(+c), dict(c=5, d=10, e=15, g=40))
 | 
					        self.assertEqual(dict(+c), dict(c=5, d=10, e=15, g=40))
 | 
				
			||||||
| 
						 | 
					@ -1308,8 +1340,11 @@ class TestOrderedDict(unittest.TestCase):
 | 
				
			||||||
                                          c=3, e=5).items()), pairs)                # mixed input
 | 
					                                          c=3, e=5).items()), pairs)                # mixed input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # make sure no positional args conflict with possible kwdargs
 | 
					        # make sure no positional args conflict with possible kwdargs
 | 
				
			||||||
        self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__']).args,
 | 
					        self.assertEqual(list(OrderedDict(self=42).items()), [('self', 42)])
 | 
				
			||||||
                         ['self'])
 | 
					        self.assertEqual(list(OrderedDict(other=42).items()), [('other', 42)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict, (), ())
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict.__init__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Make sure that direct calls to __init__ do not clear previous contents
 | 
					        # Make sure that direct calls to __init__ do not clear previous contents
 | 
				
			||||||
        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
 | 
					        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
 | 
				
			||||||
| 
						 | 
					@ -1354,6 +1389,10 @@ class TestOrderedDict(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual(list(d.items()),
 | 
					        self.assertEqual(list(d.items()),
 | 
				
			||||||
            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
 | 
					            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict().update, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict().update, (), ())
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, OrderedDict.update)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_abc(self):
 | 
					    def test_abc(self):
 | 
				
			||||||
        self.assertIsInstance(OrderedDict(), MutableMapping)
 | 
					        self.assertIsInstance(OrderedDict(), MutableMapping)
 | 
				
			||||||
        self.assertTrue(issubclass(OrderedDict, MutableMapping))
 | 
					        self.assertTrue(issubclass(OrderedDict, MutableMapping))
 | 
				
			||||||
| 
						 | 
					@ -1600,6 +1639,24 @@ class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
 | 
				
			||||||
        d = self._empty_mapping()
 | 
					        d = self._empty_mapping()
 | 
				
			||||||
        self.assertRaises(KeyError, d.popitem)
 | 
					        self.assertRaises(KeyError, d.popitem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestUserDict(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_init(self):
 | 
				
			||||||
 | 
					        self.assertEqual(list(UserDict(self=42).items()), [('self', 42)])
 | 
				
			||||||
 | 
					        self.assertEqual(list(UserDict(dict=42).items()), [('dict', 42)])
 | 
				
			||||||
 | 
					        self.assertEqual(list(UserDict(dict=None).items()), [('dict', None)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict, (), ())
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict.__init__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_update(self):
 | 
				
			||||||
 | 
					        d = UserDict()
 | 
				
			||||||
 | 
					        d.update(self=42)
 | 
				
			||||||
 | 
					        self.assertEqual(list(d.items()), [('self', 42)])
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict().update, 42)
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict().update, {}, {})
 | 
				
			||||||
 | 
					        self.assertRaises(TypeError, UserDict.update)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
### Run tests
 | 
					### Run tests
 | 
				
			||||||
| 
						 | 
					@ -1611,7 +1668,8 @@ def test_main(verbose=None):
 | 
				
			||||||
    NamedTupleDocs = doctest.DocTestSuite(module=collections)
 | 
					    NamedTupleDocs = doctest.DocTestSuite(module=collections)
 | 
				
			||||||
    test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
 | 
					    test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
 | 
				
			||||||
                    TestCollectionABCs, TestCounter, TestChainMap,
 | 
					                    TestCollectionABCs, TestCounter, TestChainMap,
 | 
				
			||||||
                    TestOrderedDict, GeneralMappingTests, SubclassMappingTests]
 | 
					                    TestOrderedDict, GeneralMappingTests, SubclassMappingTests,
 | 
				
			||||||
 | 
					                    TestUserDict,]
 | 
				
			||||||
    support.run_unittest(*test_classes)
 | 
					    support.run_unittest(*test_classes)
 | 
				
			||||||
    support.run_doctest(collections, verbose)
 | 
					    support.run_doctest(collections, verbose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,6 +191,9 @@ Core and Builtins
 | 
				
			||||||
Library
 | 
					Library
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Issue #22609: Constructors and update methods of mapping classes in the
 | 
				
			||||||
 | 
					  collections module now accept the self keyword argument.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #22940: Add readline.append_history_file.
 | 
					- Issue #22940: Add readline.append_history_file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #19676: Added the "namereplace" error handler.
 | 
					- Issue #19676: Added the "namereplace" error handler.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue