diff -r f592a4073672 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Sat Oct 11 01:43:35 2014 -0500 +++ b/Lib/collections/__init__.py Sat Oct 11 22:30:05 2014 +0300 @@ -16,6 +16,19 @@ from _weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap from reprlib import recursive_repr as _recursive_repr +def _wrap_dict_update(wrapped): + # Can't use functools.wraps due to circular import + def wrapper(*args, **kwds): + if kwds: + wrapped(*args, kwds=kwds) + else: + wrapped(*args) + for attr in ('__module__', '__name__', '__qualname__', '__doc__', + '__annotations__'): + setattr(wrapper, attr, getattr(wrapped, attr)) + del attr + return wrapper + ################################################################################ ### OrderedDict ################################################################################ @@ -55,14 +68,13 @@ class OrderedDict(dict): # Individual links are kept alive by the hard reference in self.__map. # Those hard references disappear when a key is deleted from an OrderedDict. - def __init__(self, *args, **kwds): + @_wrap_dict_update + def __init__(self, *args, kwds={}): '''Initialize an ordered dictionary. The signature is the same as regular dictionaries, but keyword arguments are not recommended because their insertion order is arbitrary. ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) try: self.__root except AttributeError: @@ -479,7 +491,8 @@ class Counter(dict): # http://code.activestate.com/recipes/259174/ # Knuth, TAOCP Vol. II section 4.6.3 - def __init__(self, iterable=None, **kwds): + @_wrap_dict_update + def __init__(self, iterable=None, *, kwds={}): '''Create a new, empty Counter object. And if given, count elements from an input iterable. Or, initialize the count from another mapping of elements to their counts. @@ -542,7 +555,8 @@ class Counter(dict): raise NotImplementedError( 'Counter.fromkeys() is undefined. Use Counter(iterable) instead.') - def update(self, iterable=None, **kwds): + @_wrap_dict_update + def update(self, iterable=None, *, kwds=None): '''Like dict.update() but add counts instead of replacing them. Source can be an iterable, a dictionary, or another Counter instance. @@ -575,7 +589,8 @@ class Counter(dict): if kwds: self.update(kwds) - def subtract(self, iterable=None, **kwds): + @_wrap_dict_update + def subtract(self, iterable=None, *, kwds=None): '''Like dict.update() but subtracts counts instead of replacing them. Counts can be reduced below zero. Both the inputs and outputs are allowed to contain zero and negative counts. @@ -898,12 +913,13 @@ class ChainMap(MutableMapping): class UserDict(MutableMapping): # Start by filling-out the abstract methods - def __init__(self, dict=None, **kwargs): + @_wrap_dict_update + def __init__(self, dict=None, *, kwds=None): self.data = {} if dict is not None: self.update(dict) - if len(kwargs): - self.update(kwargs) + if kwds: + self.update(kwds) def __len__(self): return len(self.data) def __getitem__(self, key): if key in self.data: diff -r f592a4073672 Lib/test/test_collections.py --- a/Lib/test/test_collections.py Sat Oct 11 01:43:35 2014 -0500 +++ b/Lib/test/test_collections.py Sat Oct 11 22:30:05 2014 +0300 @@ -1137,6 +1137,28 @@ class TestCounter(unittest.TestCase): self.assertEqual(c.setdefault('e', 5), 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): # Check that counters are copyable, deepcopyable, picklable, and #have a repr/eval round-trip @@ -1258,6 +1280,16 @@ class TestCounter(unittest.TestCase): c.subtract('aaaabbcce') 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): 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)) @@ -1308,8 +1340,11 @@ class TestOrderedDict(unittest.TestCase) c=3, e=5).items()), pairs) # mixed input # make sure no positional args conflict with possible kwdargs - self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__']).args, - ['self']) + self.assertEqual(list(OrderedDict(self=42).items()), [('self', 42)]) + 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 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()), [('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): self.assertIsInstance(OrderedDict(), MutableMapping) self.assertTrue(issubclass(OrderedDict, MutableMapping)) @@ -1600,6 +1639,24 @@ class SubclassMappingTests(mapping_tests d = self._empty_mapping() 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 @@ -1611,7 +1668,8 @@ def test_main(verbose=None): NamedTupleDocs = doctest.DocTestSuite(module=collections) test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs, TestCollectionABCs, TestCounter, TestChainMap, - TestOrderedDict, GeneralMappingTests, SubclassMappingTests] + TestOrderedDict, GeneralMappingTests, SubclassMappingTests, + TestUserDict,] support.run_unittest(*test_classes) support.run_doctest(collections, verbose)