Kaydet (Commit) ae5cb214 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka

Issue #22609: Constructors and update methods of mapping classes in the

collections module now accept the self keyword argument.
üst 48470354
...@@ -584,23 +584,24 @@ class MutableMapping(Mapping): ...@@ -584,23 +584,24 @@ 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:
if isinstance(other, Mapping): other = args[0]
for key in other: if isinstance(other, Mapping):
self[key] = other[key] for key in other:
elif hasattr(other, "keys"): self[key] = other[key]
for key in other.keys(): elif hasattr(other, "keys"):
self[key] = other[key] for key in other.keys():
else: self[key] = other[key]
for key, value in other: else:
self[key] = value for key, value in other:
self[key] = value
for key, value in kwds.items(): for key, value in kwds.items():
self[key] = value self[key] = value
......
...@@ -38,12 +38,16 @@ class OrderedDict(dict): ...@@ -38,12 +38,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:
...@@ -450,7 +454,7 @@ class Counter(dict): ...@@ -450,7 +454,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.
...@@ -461,8 +465,14 @@ class Counter(dict): ...@@ -461,8 +465,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.'
...@@ -513,7 +523,7 @@ class Counter(dict): ...@@ -513,7 +523,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.
...@@ -533,6 +543,13 @@ class Counter(dict): ...@@ -533,6 +543,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:
...@@ -540,13 +557,13 @@ class Counter(dict): ...@@ -540,13 +557,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.
...@@ -562,6 +579,13 @@ class Counter(dict): ...@@ -562,6 +579,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):
...@@ -869,7 +893,14 @@ class ChainMap(MutableMapping): ...@@ -869,7 +893,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)
......
...@@ -1084,6 +1084,28 @@ class TestCounter(unittest.TestCase): ...@@ -1084,6 +1084,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
...@@ -1205,6 +1227,16 @@ class TestCounter(unittest.TestCase): ...@@ -1205,6 +1227,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))
...@@ -1255,8 +1287,11 @@ class TestOrderedDict(unittest.TestCase): ...@@ -1255,8 +1287,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)])
...@@ -1301,6 +1336,10 @@ class TestOrderedDict(unittest.TestCase): ...@@ -1301,6 +1336,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))
...@@ -1532,6 +1571,24 @@ class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): ...@@ -1532,6 +1571,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
...@@ -1543,7 +1600,8 @@ def test_main(verbose=None): ...@@ -1543,7 +1600,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)
......
...@@ -36,6 +36,9 @@ Core and Builtins ...@@ -36,6 +36,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 #22788: Add *context* parameter to logging.handlers.HTTPHandler. - Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
- Issue #22921: Allow SSLContext to take the *hostname* parameter even if - Issue #22921: Allow SSLContext to take the *hostname* parameter even if
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment