Skip to content

Commit

Permalink
[3.11] pythongh-101266: Fix __sizeof__ for subclasses of int (pythonG…
Browse files Browse the repository at this point in the history
…H-101394) (python#101579)

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.

(Manual backport of python#101394 to the Python 3.11 branch.)
  • Loading branch information
mdickinson authored Feb 5, 2023
1 parent 7cbcfbe commit cf89c16
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
39 changes: 39 additions & 0 deletions Lib/test/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :func:`sys.getsizeof` reporting for :class:`int` subclasses.
6 changes: 4 additions & 2 deletions Objects/boolobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "pycore_object.h" // _Py_FatalRefcountError()
#include "pycore_runtime.h" // _Py_ID()

#include <stddef.h>

/* We define bool_repr to return "False" or "True" */

static PyObject *
Expand Down Expand Up @@ -154,8 +156,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 */
Expand Down
11 changes: 4 additions & 7 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5664,13 +5664,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, 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]
Expand Down

0 comments on commit cf89c16

Please sign in to comment.