Skip to content

Commit 426e052

Browse files
committed
Make C helper function more closely match the pure python version, and add tests.
1 parent 23eaa70 commit 426e052

2 files changed

Lines changed: 63 additions & 23 deletions

File tree

Lib/test/test_collections.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import unittest, doctest, operator
44
import inspect
55
from test import support
6-
from collections import namedtuple, Counter, OrderedDict
6+
from collections import namedtuple, Counter, OrderedDict, _count_elements
77
from test import mapping_tests
88
import pickle, copy
99
from random import randrange, shuffle
@@ -775,6 +775,19 @@ def test_subtract(self):
775775
c.subtract('aaaabbcce')
776776
self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
777777

778+
def test_helper_function(self):
779+
# two paths, one for real dicts and one for other mappings
780+
elems = list('abracadabra')
781+
782+
d = dict()
783+
_count_elements(d, elems)
784+
self.assertEqual(d, {'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1})
785+
786+
m = OrderedDict()
787+
_count_elements(m, elems)
788+
self.assertEqual(m,
789+
OrderedDict([('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)]))
790+
778791
class TestOrderedDict(unittest.TestCase):
779792

780793
def test_init(self):

Modules/_collectionsmodule.c

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,41 +1536,68 @@ _count_elements(PyObject *self, PyObject *args)
15361536
if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable))
15371537
return NULL;
15381538

1539-
if (!PyDict_Check(mapping)) {
1540-
PyErr_SetString(PyExc_TypeError,
1541-
"Expected mapping argument to be a dictionary");
1542-
return NULL;
1543-
}
1544-
15451539
it = PyObject_GetIter(iterable);
15461540
if (it == NULL)
15471541
return NULL;
1542+
15481543
one = PyLong_FromLong(1);
15491544
if (one == NULL) {
15501545
Py_DECREF(it);
15511546
return NULL;
15521547
}
1553-
while (1) {
1554-
key = PyIter_Next(it);
1555-
if (key == NULL) {
1556-
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration))
1557-
PyErr_Clear();
1558-
break;
1548+
1549+
if (PyDict_CheckExact(mapping)) {
1550+
while (1) {
1551+
key = PyIter_Next(it);
1552+
if (key == NULL) {
1553+
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration))
1554+
PyErr_Clear();
1555+
else
1556+
break;
1557+
}
1558+
oldval = PyDict_GetItem(mapping, key);
1559+
if (oldval == NULL) {
1560+
if (PyDict_SetItem(mapping, key, one) == -1)
1561+
break;
1562+
} else {
1563+
newval = PyNumber_Add(oldval, one);
1564+
if (newval == NULL)
1565+
break;
1566+
if (PyDict_SetItem(mapping, key, newval) == -1)
1567+
break;
1568+
Py_CLEAR(newval);
1569+
}
1570+
Py_DECREF(key);
15591571
}
1560-
oldval = PyDict_GetItem(mapping, key);
1561-
if (oldval == NULL) {
1562-
if (PyDict_SetItem(mapping, key, one) == -1)
1563-
break;
1564-
} else {
1565-
newval = PyNumber_Add(oldval, one);
1566-
if (newval == NULL)
1567-
break;
1568-
if (PyDict_SetItem(mapping, key, newval) == -1)
1572+
} else {
1573+
while (1) {
1574+
key = PyIter_Next(it);
1575+
if (key == NULL) {
1576+
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration))
1577+
PyErr_Clear();
1578+
else
1579+
break;
1580+
}
1581+
oldval = PyObject_GetItem(mapping, key);
1582+
if (oldval == NULL) {
1583+
if (!PyErr_Occurred() || !PyErr_ExceptionMatches(PyExc_KeyError))
1584+
break;
1585+
PyErr_Clear();
1586+
Py_INCREF(one);
1587+
newval = one;
1588+
} else {
1589+
newval = PyNumber_Add(oldval, one);
1590+
Py_DECREF(oldval);
1591+
if (newval == NULL)
1592+
break;
1593+
}
1594+
if (PyObject_SetItem(mapping, key, newval) == -1)
15691595
break;
15701596
Py_CLEAR(newval);
1597+
Py_DECREF(key);
15711598
}
1572-
Py_DECREF(key);
15731599
}
1600+
15741601
Py_DECREF(it);
15751602
Py_XDECREF(key);
15761603
Py_XDECREF(newval);

0 commit comments

Comments
 (0)