diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 7a1ff14b172..eaceb97af3d 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -1696,6 +1696,15 @@ class OrderedDictTests: self.assertRaises(TypeError, OrderedDict().update, (), ()) self.assertRaises(TypeError, OrderedDict.update) + def test_fromkeys(self): + OrderedDict = self.module.OrderedDict + od = OrderedDict.fromkeys('abc') + self.assertEqual(list(od.items()), [(c, None) for c in 'abc']) + od = OrderedDict.fromkeys('abc', value=None) + self.assertEqual(list(od.items()), [(c, None) for c in 'abc']) + od = OrderedDict.fromkeys('abc', value=0) + self.assertEqual(list(od.items()), [(c, 0) for c in 'abc']) + def test_abc(self): OrderedDict = self.module.OrderedDict self.assertIsInstance(OrderedDict(), MutableMapping) @@ -1801,7 +1810,8 @@ class OrderedDictTests: for i in range(8): obj.popitem(True) obj.popitem(True) - self.assertEqual(len(obj), 21) + obj.popitem(last=True) + self.assertEqual(len(obj), 20) def test_pop(self): OrderedDict = self.module.OrderedDict @@ -1825,6 +1835,7 @@ class OrderedDictTests: self.assertEqual(m.pop('b', 5), 5) self.assertEqual(m.pop('a', 6), 1) self.assertEqual(m.pop('a', 6), 6) + self.assertEqual(m.pop('a', default=6), 6) with self.assertRaises(KeyError): m.pop('a') @@ -1931,6 +1942,7 @@ class OrderedDictTests: self.assertEqual(od.setdefault('x', 10), 10) # make sure 'x' is added to the end self.assertEqual(list(od.items())[-1], ('x', 10)) + self.assertEqual(od.setdefault('g', default=9), 9) # make sure setdefault still works when __missing__ is defined class Missing(OrderedDict): @@ -1962,6 +1974,8 @@ class OrderedDictTests: self.assertEqual(list(od), list('cabde')) od.move_to_end('e') self.assertEqual(list(od), list('cabde')) + od.move_to_end('b', last=False) + self.assertEqual(list(od), list('bcade')) with self.assertRaises(KeyError): od.move_to_end('x') with self.assertRaises(KeyError): diff --git a/Misc/NEWS b/Misc/NEWS index c0990ce531d..a731b6280f5 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Library - Issue #24359: Check for changed OrderedDict size during iteration. +- Issue #24368: Support keyword arguments in OrderedDict methods. + What's New in Python 3.5.0 beta 2? ================================== diff --git a/Objects/odictobject.c b/Objects/odictobject.c index b44a61ef75a..fa339e10b69 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -930,12 +930,17 @@ PyDoc_STRVAR(odict_fromkeys__doc__, "); static PyObject * -odict_fromkeys(PyObject *cls, PyObject *args) +odict_fromkeys(PyObject *cls, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"iterable", "value", 0}; PyObject *seq; PyObject *value = Py_None; - if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &seq, &value)) /* borrowed */ + + /* both borrowed */ + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:fromkeys", kwlist, + &seq, &value)) { return NULL; + } return _PyDict_FromKeys(cls, seq, value); } @@ -1071,14 +1076,17 @@ PyDoc_STRVAR(odict_setdefault__doc__, /* Skips __missing__() calls. */ static PyObject * -odict_setdefault(register PyODictObject *od, PyObject *args) +odict_setdefault(register PyODictObject *od, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"key", "default", 0}; PyObject *key, *result = NULL; PyObject *failobj = Py_None; /* both borrowed */ - if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:setdefault", kwlist, + &key, &failobj)) { return NULL; + } if (PyODict_CheckExact(od)) { result = PyODict_GetItemWithError(od, key); /* borrowed */ @@ -1126,11 +1134,14 @@ static PyObject * _odict_popkey(PyObject *, PyObject *, PyObject *); /* Skips __missing__() calls. */ static PyObject * -odict_pop(PyObject *od, PyObject *args) +odict_pop(PyObject *od, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"key", "default", 0}; PyObject *key, *failobj = NULL; - if (!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &failobj)) { /* borrowed */ + /* borrowed */ + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:pop", kwlist, + &key, &failobj)) { return NULL; } @@ -1202,10 +1213,12 @@ PyDoc_STRVAR(odict_popitem__doc__, "); static PyObject * -odict_popitem(PyObject *od, PyObject *args) +odict_popitem(PyObject *od, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"last", 0}; PyObject *key, *value, *item = NULL, *last = NULL; _ODictNode *node; + int pos = -1; if (_odict_EMPTY((PyODictObject *)od)) { PyErr_SetString(PyExc_KeyError, "dictionary is empty"); @@ -1214,13 +1227,23 @@ odict_popitem(PyObject *od, PyObject *args) /* pull the item */ - if (!PyArg_UnpackTuple(args, "popitem", 0, 1, &last)) /* borrowed */ + /* borrowed */ + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:popitem", kwlist, + &last)) { return NULL; + } - if (last == NULL || last == Py_True) - node = _odict_LAST((PyODictObject *)od); - else + if (last != NULL) { + int is_true; + is_true = PyObject_IsTrue(last); + if (is_true == -1) + return NULL; + pos = is_true ? -1 : 0; + } + if (pos == 0) node = _odict_FIRST((PyODictObject *)od); + else + node = _odict_LAST((PyODictObject *)od); key = _odictnode_KEY(node); Py_INCREF(key); @@ -1360,14 +1383,17 @@ PyDoc_STRVAR(odict_move_to_end__doc__, "); static PyObject * -odict_move_to_end(PyODictObject *od, PyObject *args) +odict_move_to_end(PyODictObject *od, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"key", "last", 0}; PyObject *key, *last = NULL; Py_ssize_t pos = -1; /* both borrowed */ - if (!PyArg_UnpackTuple(args, "move_to_end", 1, 2, &key, &last)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:move_to_end", kwlist, + &key, &last)) { return NULL; + } if (_odict_EMPTY(od)) { PyErr_SetObject(PyExc_KeyError, key); return NULL; @@ -1439,20 +1465,20 @@ static PyMethodDef odict_methods[] = { odict_repr__doc__}, {"__setitem__", (PyCFunction)odict_mp_ass_sub, METH_NOARGS, odict_setitem__doc__}, - {"fromkeys", (PyCFunction)odict_fromkeys, METH_VARARGS | METH_CLASS, - odict_fromkeys__doc__}, + {"fromkeys", (PyCFunction)odict_fromkeys, + METH_VARARGS | METH_KEYWORDS | METH_CLASS, odict_fromkeys__doc__}, /* overridden dict methods */ {"__sizeof__", (PyCFunction)odict_sizeof, METH_NOARGS, odict_sizeof__doc__}, {"__reduce__", (PyCFunction)odict_reduce, METH_NOARGS, odict_reduce__doc__}, - {"setdefault", (PyCFunction)odict_setdefault, METH_VARARGS, - odict_setdefault__doc__}, - {"pop", (PyCFunction)odict_pop, METH_VARARGS, - odict_pop__doc__}, - {"popitem", (PyCFunction)odict_popitem, METH_VARARGS, - odict_popitem__doc__}, + {"setdefault", (PyCFunction)odict_setdefault, + METH_VARARGS | METH_KEYWORDS, odict_setdefault__doc__}, + {"pop", (PyCFunction)odict_pop, + METH_VARARGS | METH_KEYWORDS, odict_pop__doc__}, + {"popitem", (PyCFunction)odict_popitem, + METH_VARARGS | METH_KEYWORDS, odict_popitem__doc__}, {"keys", (PyCFunction)odictkeys_new, METH_NOARGS, odict_keys__doc__}, {"values", (PyCFunction)odictvalues_new, METH_NOARGS, @@ -1469,8 +1495,8 @@ static PyMethodDef odict_methods[] = { /* new methods */ {"__reversed__", (PyCFunction)odict_reversed, METH_NOARGS, odict_reversed__doc__}, - {"move_to_end", (PyCFunction)odict_move_to_end, METH_VARARGS, - odict_move_to_end__doc__}, + {"move_to_end", (PyCFunction)odict_move_to_end, + METH_VARARGS | METH_KEYWORDS, odict_move_to_end__doc__}, {NULL, NULL} /* sentinel */ };