mirror of https://github.com/python/cpython
gh-101266: Fix __sizeof__ for subclasses of int (#101394)
Fix the behaviour of the `__sizeof__` method (and hence the results returned by `sys.getsizeof`) for subclasses of `int`. Previously, `int` subclasses gave identical results to the `int` base class, ignoring the presence of the instance dictionary. <!-- gh-issue-number: gh-101266 --> * Issue: gh-101266 <!-- /gh-issue-number -->
This commit is contained in:
parent
9b60ee976a
commit
39017e04b5
|
@ -1601,5 +1601,44 @@ class LongTest(unittest.TestCase):
|
||||||
self.assertEqual(n**2,
|
self.assertEqual(n**2,
|
||||||
(1 << (2 * bitlen)) - (1 << (bitlen + 1)) + 1)
|
(1 << (2 * bitlen)) - (1 << (bitlen + 1)) + 1)
|
||||||
|
|
||||||
|
def test___sizeof__(self):
|
||||||
|
self.assertEqual(int.__itemsize__, sys.int_info.sizeof_digit)
|
||||||
|
|
||||||
|
# Pairs (test_value, number of allocated digits)
|
||||||
|
test_values = [
|
||||||
|
# We always allocate space for at least one digit, even for
|
||||||
|
# a value of zero; sys.getsizeof should reflect that.
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
(-1, 1),
|
||||||
|
(BASE-1, 1),
|
||||||
|
(1-BASE, 1),
|
||||||
|
(BASE, 2),
|
||||||
|
(-BASE, 2),
|
||||||
|
(BASE*BASE - 1, 2),
|
||||||
|
(BASE*BASE, 3),
|
||||||
|
]
|
||||||
|
|
||||||
|
for value, ndigits in test_values:
|
||||||
|
with self.subTest(value):
|
||||||
|
self.assertEqual(
|
||||||
|
value.__sizeof__(),
|
||||||
|
int.__basicsize__ + int.__itemsize__ * ndigits
|
||||||
|
)
|
||||||
|
|
||||||
|
# Same test for a subclass of int.
|
||||||
|
class MyInt(int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(MyInt.__itemsize__, sys.int_info.sizeof_digit)
|
||||||
|
|
||||||
|
for value, ndigits in test_values:
|
||||||
|
with self.subTest(value):
|
||||||
|
self.assertEqual(
|
||||||
|
MyInt(value).__sizeof__(),
|
||||||
|
MyInt.__basicsize__ + MyInt.__itemsize__ * ndigits
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses.
|
|
@ -4,6 +4,8 @@
|
||||||
#include "pycore_object.h" // _Py_FatalRefcountError()
|
#include "pycore_object.h" // _Py_FatalRefcountError()
|
||||||
#include "pycore_runtime.h" // _Py_ID()
|
#include "pycore_runtime.h" // _Py_ID()
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
/* We define bool_repr to return "False" or "True" */
|
/* We define bool_repr to return "False" or "True" */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -153,8 +155,8 @@ bool_dealloc(PyObject* Py_UNUSED(ignore))
|
||||||
PyTypeObject PyBool_Type = {
|
PyTypeObject PyBool_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"bool",
|
"bool",
|
||||||
sizeof(struct _longobject),
|
offsetof(struct _longobject, long_value.ob_digit), /* tp_basicsize */
|
||||||
0,
|
sizeof(digit), /* tp_itemsize */
|
||||||
bool_dealloc, /* tp_dealloc */
|
bool_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_vectorcall_offset */
|
0, /* tp_vectorcall_offset */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
|
|
|
@ -5882,13 +5882,10 @@ static Py_ssize_t
|
||||||
int___sizeof___impl(PyObject *self)
|
int___sizeof___impl(PyObject *self)
|
||||||
/*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/
|
/*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/
|
||||||
{
|
{
|
||||||
Py_ssize_t res;
|
/* using Py_MAX(..., 1) because we always allocate space for at least
|
||||||
|
one digit, even though the integer zero has a Py_SIZE of 0 */
|
||||||
res = offsetof(PyLongObject, long_value.ob_digit)
|
Py_ssize_t ndigits = Py_MAX(Py_ABS(Py_SIZE(self)), 1);
|
||||||
/* using Py_MAX(..., 1) because we always allocate space for at least
|
return Py_TYPE(self)->tp_basicsize + Py_TYPE(self)->tp_itemsize * ndigits;
|
||||||
one digit, even though the integer zero has a Py_SIZE of 0 */
|
|
||||||
+ Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
|
Loading…
Reference in New Issue