diff --git a/Include/dictobject.h b/Include/dictobject.h index 80bd3302949..ba90aaf676c 100644 --- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -98,7 +98,7 @@ PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused); PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp); PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp); Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys); -PyObject *_PyDict_SizeOf(PyDictObject *); +Py_ssize_t _PyDict_SizeOf(PyDictObject *); PyObject *_PyDict_Pop(PyDictObject *, PyObject *, PyObject *); PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *); #define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 4b092279692..9d6c6ffa221 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -2,6 +2,7 @@ import contextlib import copy import pickle from random import randrange, shuffle +import struct import sys import unittest from collections.abc import MutableMapping @@ -596,6 +597,37 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): module = c_coll OrderedDict = c_coll.OrderedDict + check_sizeof = support.check_sizeof + + @support.cpython_only + def test_sizeof_exact(self): + OrderedDict = self.OrderedDict + calcsize = struct.calcsize + size = support.calcobjsize + check = self.check_sizeof + + basicsize = size('n2P' + '3PnPn2P') + calcsize('2nPn') + entrysize = calcsize('n2P') + calcsize('P') + nodesize = calcsize('Pn2P') + + od = OrderedDict() + check(od, basicsize + 8*entrysize) + od.x = 1 + check(od, basicsize + 8*entrysize) + od.update([(i, i) for i in range(3)]) + check(od, basicsize + 8*entrysize + 3*nodesize) + od.update([(i, i) for i in range(3, 10)]) + check(od, basicsize + 16*entrysize + 10*nodesize) + + check(od.keys(), size('P')) + check(od.items(), size('P')) + check(od.values(), size('P')) + + itersize = size('iP2n2P') + check(iter(od), itersize) + check(iter(od.keys()), itersize) + check(iter(od.items()), itersize) + check(iter(od.values()), itersize) def test_key_change_during_iteration(self): OrderedDict = self.OrderedDict diff --git a/Misc/NEWS b/Misc/NEWS index ef37f5f000d..3257aa4a033 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,6 +28,8 @@ Core and Builtins Library ------- +- Issue #25914: Fixed and simplified OrderedDict.__sizeof__. + - Issue #25902: Fixed various refcount issues in ElementTree iteration. - Issue #25717: Restore the previous behaviour of tolerating most fstat() diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 3e6e112ba04..f53cd407962 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2554,7 +2554,7 @@ dict_tp_clear(PyObject *op) static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); -PyObject * +Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, res; @@ -2567,7 +2567,7 @@ _PyDict_SizeOf(PyDictObject *mp) in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry); - return PyLong_FromSsize_t(res); + return res; } Py_ssize_t @@ -2576,6 +2576,12 @@ _PyDict_KeysSize(PyDictKeysObject *keys) return sizeof(PyDictKeysObject) + (DK_SIZE(keys)-1) * sizeof(PyDictKeyEntry); } +PyObject * +dict_sizeof(PyDictObject *mp) +{ + return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); +} + PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, @@ -2623,7 +2629,7 @@ static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, - {"__sizeof__", (PyCFunction)_PyDict_SizeOf, METH_NOARGS, + {"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS, sizeof__doc__}, {"get", (PyCFunction)dict_get, METH_VARARGS, get__doc__}, diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 4e51f4d1475..c15b408dc24 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -940,27 +940,7 @@ PyDoc_STRVAR(odict_sizeof__doc__, ""); static PyObject * odict_sizeof(PyODictObject *od) { - PyObject *pylong; - Py_ssize_t res, temp; - - pylong = _PyDict_SizeOf((PyDictObject *)od); - if (pylong == NULL) - return NULL; - res = PyLong_AsSsize_t(pylong); - Py_DECREF(pylong); - if (res == -1 && PyErr_Occurred()) - return NULL; - - /* instance dict */ - pylong = _PyDict_SizeOf((PyDictObject *)od->od_inst_dict); - if (pylong == NULL) - return NULL; - temp = PyLong_AsSsize_t(pylong); - Py_DECREF(pylong); - if (temp == -1 && PyErr_Occurred()) - return NULL; - res += temp; - + Py_ssize_t res = _PyDict_SizeOf((PyDictObject *)od); res += sizeof(_ODictNode *) * _odict_FAST_SIZE(od); /* od_fast_nodes */ if (!_odict_EMPTY(od)) { res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */