From d77002e8d76a9c813a94d2d63edb1a936818e997 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:23:04 +0000 Subject: [PATCH 1/5] Fix __sizeof__ for subclasses of int (cherry picked from commit c345b90a45f8ef40c6b544199d3dc7ac35abba2f) --- 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 77b37ca1fa4afe..03bed4c41f5a0c 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1596,5 +1596,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 84c05e8aabdfda..f3951dee317d69 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5664,13 +5664,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 37a2ba57db423fcc3625b79292162a3cd7a58077 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:27:11 +0000 Subject: [PATCH 2/5] Fix markup (cherry picked from commit d8c8f32864690933adf5b5d443e7b0f9b10e4c2a) --- .../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 c310017b14faef2dd2255cb64da200df9a0fedf3 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 13:28:36 +0000 Subject: [PATCH 3/5] Preserve existing comment (cherry picked from commit f46ba550ae00dbe9edaa0ad844de1cb1c14743b1) --- Objects/longobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/longobject.c b/Objects/longobject.c index f3951dee317d69..39bd72ce5598d1 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5664,6 +5664,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 72038d085f71ad9017ed449b78d04a591d03523f Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 14:28:26 +0000 Subject: [PATCH 4/5] Fix tp_basicsize and tp_itemsize for bool (cherry picked from commit b7eba006d8d442919c30d0bd042fbb2174022fb8) --- Objects/boolobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 8a20e368d4a42b..6be43fa465216e 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -154,8 +154,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 09a9a7155f45b64582b02d98a41a5da8a003f46c Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 28 Jan 2023 14:32:59 +0000 Subject: [PATCH 5/5] Fix missing include (cherry picked from commit 9cd5d01754f1faaca5ddbf504ac981df6427f9e5) --- Objects/boolobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 6be43fa465216e..01acf0f00b66fe 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 *