From 8be5b3bdb541d0522bff57b25fb6b42a414b1b8b Mon Sep 17 00:00:00 2001 From: Bruno Cauet Date: Wed, 11 Mar 2015 18:54:32 +0100 Subject: [PATCH] int.from_bytes() calls constructor for subclasses When called from a subclass, int.from_bytes() instantiates (new + init) the subclass with the value retrieved instead of copying its bytes. This allow __new__ & __init__ statements to apply, which has consequences e.g. on enum.IntEnum children. Fix issue 23640. --- Lib/test/test_long.py | 13 +++++++++++++ Objects/longobject.c | 21 ++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 57847d8..95525b4 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1219,6 +1219,19 @@ class LongTest(unittest.TestCase): self.assertRaises(TypeError, myint.from_bytes, 0, 'big') self.assertRaises(TypeError, int.from_bytes, 0, 'big', True) + class odd(int): + def __init__(self, x, base=10): + if not x % 2: + raise ValueError("No even numbers accepted") + super(odd, self).__init__() + + o = odd.from_bytes(b'\1', 'big') + self.assertIs(type(o), odd) + self.assertEqual(o, 1) + with self.assertRaises(ValueError) as exc_context: + odd.from_bytes(b'\2', 'big') + self.assertIn("even numbers", str(exc_context.exception)) + def test_access_to_nonexistent_digit_0(self): # http://bugs.python.org/issue14630: A bug in _PyLong_Copy meant that # ob_digit[0] was being incorrectly accessed for instances of a diff --git a/Objects/longobject.c b/Objects/longobject.c index 27bee50..33ce219 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4846,23 +4846,22 @@ long_from_bytes(PyTypeObject *type, PyObject *args, PyObject *kwds) * instance, initialize it with decoded int value and return it. */ if (type != &PyLong_Type && PyType_IsSubtype(type, &PyLong_Type)) { - PyLongObject *newobj; - int i; - Py_ssize_t n = Py_ABS(Py_SIZE(long_obj)); + PyObject *args, *newobj; - newobj = (PyLongObject *)type->tp_alloc(type, n); - if (newobj == NULL) { + args = PyTuple_New(1); + if(args == NULL) { Py_DECREF(long_obj); return NULL; } - assert(PyLong_Check(newobj)); - Py_SIZE(newobj) = Py_SIZE(long_obj); - for (i = 0; i < n; i++) { - newobj->ob_digit[i] = - ((PyLongObject *)long_obj)->ob_digit[i]; + if(PyTuple_SetItem(args, 0, long_obj) == -1) { + Py_DECREF(long_obj); + return NULL; } + + newobj = PyObject_Call(type, args, NULL); Py_DECREF(long_obj); - return (PyObject *)newobj; + Py_DECREF(args); + return newobj; } return long_obj; -- 2.3.1