From c345b90a45f8ef40c6b544199d3dc7ac35abba2f Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:23:04 +0000 Subject: [PATCH 1/6] Fix __sizeof__ for subclasses of int --- Lib/test/test_long.py | 39 +++++++++++++++++++ ...-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 1 + Objects/longobject.c | 9 +---- 3 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst 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..315bd13fbc3755 --- /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 `int` subclasses. diff --git a/Objects/longobject.c b/Objects/longobject.c index 51ac86961c9954..b6bf5bd2167a35 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5882,13 +5882,8 @@ static Py_ssize_t int___sizeof___impl(PyObject *self) /*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/ { - Py_ssize_t res; - - res = offsetof(PyLongObject, 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; + 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] From d8c8f32864690933adf5b5d443e7b0f9b10e4c2a Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:27:11 +0000 Subject: [PATCH 2/6] Fix markup --- .../2023-01-28-13-11-52.gh-issue-101266.AxV3OF.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 315bd13fbc3755..51999bacb8de07 100644 --- 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 @@ -1 +1 @@ -Fix :func:`sys.getsizeof` reporting for `int` subclasses. +Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses. From f46ba550ae00dbe9edaa0ad844de1cb1c14743b1 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:28:36 +0000 Subject: [PATCH 3/6] Preserve existing comment --- Objects/longobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/longobject.c b/Objects/longobject.c index b6bf5bd2167a35..708e674e5f2f91 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5882,6 +5882,8 @@ static Py_ssize_t int___sizeof___impl(PyObject *self) /*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/ { + /* 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; } From b7eba006d8d442919c30d0bd042fbb2174022fb8 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 14:28:26 +0000 Subject: [PATCH 4/6] Fix tp_basicsize and tp_itemsize for bool --- Objects/boolobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 18666f88cbde10..50214152e802ed 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -153,8 +153,8 @@ bool_dealloc(PyObject* Py_UNUSED(ignore)) PyTypeObject PyBool_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bool", - sizeof(struct _longobject), - 0, + offsetof(struct _longobject, ob_digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ bool_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ From 9cd5d01754f1faaca5ddbf504ac981df6427f9e5 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 14:32:59 +0000 Subject: [PATCH 5/6] Fix missing include --- Objects/boolobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 50214152e802ed..f79b463124fd29 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 * From ae6229b9b491f273fc86f4aadb532ef1279b8828 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 30 Jan 2023 17:33:05 +0000 Subject: [PATCH 6/6] Update for recent changes in longobject.c --- Objects/boolobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 5260460fa468cb..a035f463323823 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -155,7 +155,7 @@ bool_dealloc(PyObject* Py_UNUSED(ignore)) PyTypeObject PyBool_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bool", - offsetof(struct _longobject, ob_digit), /* tp_basicsize */ + offsetof(struct _longobject, long_value.ob_digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ bool_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */