diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 53a80f4b473..8158f783cc5 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -779,6 +779,14 @@ class BytesTest(BaseBytesTest, unittest.TestCase): def __index__(self): return 42 self.assertEqual(bytes(A()), b'a') + # Issue #24731 + class A: + def __bytes__(self): + return OtherBytesSubclass(b'abc') + self.assertEqual(bytes(A()), b'abc') + self.assertIs(type(bytes(A())), OtherBytesSubclass) + self.assertEqual(BytesSubclass(A()), b'abc') + self.assertIs(type(BytesSubclass(A())), BytesSubclass) # Test PyBytes_FromFormat() def test_from_format(self): @@ -1552,6 +1560,9 @@ class ByteArraySubclass(bytearray): class BytesSubclass(bytes): pass +class OtherBytesSubclass(bytes): + pass + class ByteArraySubclassTest(SubclassTest, unittest.TestCase): type2test = bytearray subclass2test = ByteArraySubclass diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 723beb37623..48f7a70c680 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -25,6 +25,12 @@ requires_setformat = unittest.skipUnless(hasattr(float, "__setformat__"), test_dir = os.path.dirname(__file__) or os.curdir format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt') +class FloatSubclass(float): + pass + +class OtherFloatSubclass(float): + pass + class GeneralFloatCases(unittest.TestCase): def test_float(self): @@ -167,6 +173,15 @@ class GeneralFloatCases(unittest.TestCase): return "" self.assertRaises(TypeError, time.sleep, Foo5()) + # Issue #24731 + class F: + def __float__(self): + return OtherFloatSubclass(42.) + self.assertAlmostEqual(float(F()), 42.) + self.assertIs(type(float(F())), OtherFloatSubclass) + self.assertAlmostEqual(FloatSubclass(F()), 42.) + self.assertIs(type(FloatSubclass(F())), FloatSubclass) + def test_is_integer(self): self.assertFalse((1.1).is_integer()) self.assertTrue((1.).is_integer()) diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 3e4b4fc2fdc..b66c5d6709b 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -24,6 +24,9 @@ L = [ ("\u0200", ValueError) ] +class IntSubclass(int): + pass + class IntTestCases(unittest.TestCase): def test_basic(self): @@ -441,6 +444,10 @@ class IntTestCases(unittest.TestCase): good_int = TruncReturnsIntSubclass() n = int(good_int) self.assertEqual(n, 1) + self.assertIs(type(n), bool) + n = IntSubclass(good_int) + self.assertEqual(n, 1) + self.assertIs(type(n), IntSubclass) def test_error_message(self): def check(s, base=None): diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 56a60df0077..ce8df40d161 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -43,6 +43,9 @@ def duplicate_string(text): """ return text.encode().decode() +class StrSubclass(str): + pass + class UnicodeTest(string_tests.CommonTest, string_tests.MixinStrUnicodeUserStringTest, string_tests.MixinStrUnicodeTest, @@ -1441,11 +1444,8 @@ class UnicodeTest(string_tests.CommonTest, 'unicode remains unicode' ) - class UnicodeSubclass(str): - pass - for text in ('ascii', '\xe9', '\u20ac', '\U0010FFFF'): - subclass = UnicodeSubclass(text) + subclass = StrSubclass(text) self.assertEqual(str(subclass), text) self.assertEqual(len(subclass), len(text)) if text == 'ascii': @@ -2199,6 +2199,9 @@ class UnicodeTest(string_tests.CommonTest, s = str(StrSubclassToStrSubclass("foo")) self.assertEqual(s, "foofoo") self.assertIs(type(s), StrSubclassToStrSubclass) + s = StrSubclass(StrSubclassToStrSubclass("foo")) + self.assertEqual(s, "foofoo") + self.assertIs(type(s), StrSubclass) def test_unicode_repr(self): class s1: diff --git a/Misc/NEWS b/Misc/NEWS index 97e2ecd9f52..99e962be28a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ Release date: XXXX-XX-XX Core and Builtins ----------------- +- Issue #24731: Fixed crash on converting objects with special methods + __bytes__, __trunc__, and __float__ returning instances of subclasses of + bytes, int, and float to subclasses of bytes, int, and float correspondingly. + Library ------- diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 08275ad5ada..8e098494de6 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3146,7 +3146,7 @@ static PyNumberMethods bytes_as_number = { }; static PyObject * -str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); +bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static PyObject * bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -3161,7 +3161,7 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) _Py_IDENTIFIER(__bytes__); if (type != &PyBytes_Type) - return str_subtype_new(type, args, kwds); + return bytes_subtype_new(type, args, kwds); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, &x, &encoding, &errors)) return NULL; @@ -3388,7 +3388,7 @@ PyBytes_FromObject(PyObject *x) } static PyObject * -str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *tmp, *pnew; Py_ssize_t n; @@ -3397,7 +3397,7 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) tmp = bytes_new(&PyBytes_Type, args, kwds); if (tmp == NULL) return NULL; - assert(PyBytes_CheckExact(tmp)); + assert(PyBytes_Check(tmp)); n = PyBytes_GET_SIZE(tmp); pnew = type->tp_alloc(type, n); if (pnew != NULL) { diff --git a/Objects/floatobject.c b/Objects/floatobject.c index b8d6f2b52e0..d92bec35b5f 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1568,7 +1568,7 @@ float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) tmp = float_new(&PyFloat_Type, args, kwds); if (tmp == NULL) return NULL; - assert(PyFloat_CheckExact(tmp)); + assert(PyFloat_Check(tmp)); newobj = type->tp_alloc(type, 0); if (newobj == NULL) { Py_DECREF(tmp); diff --git a/Objects/longobject.c b/Objects/longobject.c index 5f224557d1f..d821e4bfb67 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4611,7 +4611,7 @@ long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) tmp = (PyLongObject *)long_new(&PyLong_Type, args, kwds); if (tmp == NULL) return NULL; - assert(PyLong_CheckExact(tmp)); + assert(PyLong_Check(tmp)); n = Py_SIZE(tmp); if (n < 0) n = -n;