diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 569ab15820e302..d299c34cec076d 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1601,5 +1601,44 @@ def test_square(self): self.assertEqual(n**2, (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__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst new file mode 100644 index 00000000000000..51999bacb8de07 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst @@ -0,0 +1 @@ +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 55b4a4077ab783..a035f463323823 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -4,6 +4,8 @@ #include "pycore_object.h" // _Py_FatalRefcountError() #include "pycore_runtime.h" // _Py_ID() +#include + /* We define bool_repr to return "False" or "True" */ static PyObject * @@ -153,8 +155,8 @@ bool_dealloc(PyObject* Py_UNUSED(ignore)) PyTypeObject PyBool_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bool", - sizeof(struct _longobject), - 0, + offsetof(struct _longobject, long_value.ob_digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ bool_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ diff --git a/Objects/longobject.c b/Objects/longobject.c index 65bf15648b07fb..8293f133bed213 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5882,13 +5882,10 @@ static Py_ssize_t int___sizeof___impl(PyObject *self) /*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/ { - Py_ssize_t res; - - res = offsetof(PyLongObject, long_value.ob_digit) - /* 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 */ - + Py_MAX(Py_ABS(Py_SIZE(self)), 1)*sizeof(digit); - return 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 */ + Py_ssize_t ndigits = Py_MAX(Py_ABS(Py_SIZE(self)), 1); + return Py_TYPE(self)->tp_basicsize + Py_TYPE(self)->tp_itemsize * ndigits; } /*[clinic input]