Skip to content

Commit 1509580

Browse files
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.
1 parent a49de6b commit 1509580

File tree

8 files changed

+50
-10
lines changed

8 files changed

+50
-10
lines changed

Lib/test/test_bytes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,14 @@ def __bytes__(self):
744744
def __index__(self):
745745
return 42
746746
self.assertEqual(bytes(A()), b'a')
747+
# Issue #24731
748+
class A:
749+
def __bytes__(self):
750+
return OtherBytesSubclass(b'abc')
751+
self.assertEqual(bytes(A()), b'abc')
752+
self.assertIs(type(bytes(A())), OtherBytesSubclass)
753+
self.assertEqual(BytesSubclass(A()), b'abc')
754+
self.assertIs(type(BytesSubclass(A())), BytesSubclass)
747755

748756
# Test PyBytes_FromFormat()
749757
def test_from_format(self):
@@ -1465,6 +1473,9 @@ class ByteArraySubclass(bytearray):
14651473
class BytesSubclass(bytes):
14661474
pass
14671475

1476+
class OtherBytesSubclass(bytes):
1477+
pass
1478+
14681479
class ByteArraySubclassTest(SubclassTest, unittest.TestCase):
14691480
type2test = bytearray
14701481
subclass2test = ByteArraySubclass

Lib/test/test_float.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
test_dir = os.path.dirname(__file__) or os.curdir
2626
format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt')
2727

28+
class FloatSubclass(float):
29+
pass
30+
31+
class OtherFloatSubclass(float):
32+
pass
33+
2834
class GeneralFloatCases(unittest.TestCase):
2935

3036
def test_float(self):
@@ -167,6 +173,15 @@ def __float__(self):
167173
return ""
168174
self.assertRaises(TypeError, time.sleep, Foo5())
169175

176+
# Issue #24731
177+
class F:
178+
def __float__(self):
179+
return OtherFloatSubclass(42.)
180+
self.assertAlmostEqual(float(F()), 42.)
181+
self.assertIs(type(float(F())), OtherFloatSubclass)
182+
self.assertAlmostEqual(FloatSubclass(F()), 42.)
183+
self.assertIs(type(FloatSubclass(F())), FloatSubclass)
184+
170185
def test_is_integer(self):
171186
self.assertFalse((1.1).is_integer())
172187
self.assertTrue((1.).is_integer())

Lib/test/test_int.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
("\u0200", ValueError)
2525
]
2626

27+
class IntSubclass(int):
28+
pass
29+
2730
class IntTestCases(unittest.TestCase):
2831

2932
def test_basic(self):
@@ -441,6 +444,10 @@ def __trunc__(self):
441444
good_int = TruncReturnsIntSubclass()
442445
n = int(good_int)
443446
self.assertEqual(n, 1)
447+
self.assertIs(type(n), bool)
448+
n = IntSubclass(good_int)
449+
self.assertEqual(n, 1)
450+
self.assertIs(type(n), IntSubclass)
444451

445452
def test_error_message(self):
446453
def check(s, base=None):

Lib/test/test_unicode.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ def duplicate_string(text):
4242
"""
4343
return text.encode().decode()
4444

45+
class StrSubclass(str):
46+
pass
47+
4548
class UnicodeTest(string_tests.CommonTest,
4649
string_tests.MixinStrUnicodeUserStringTest,
4750
string_tests.MixinStrUnicodeTest,
@@ -1412,11 +1415,8 @@ def test_constructor(self):
14121415
'unicode remains unicode'
14131416
)
14141417

1415-
class UnicodeSubclass(str):
1416-
pass
1417-
14181418
for text in ('ascii', '\xe9', '\u20ac', '\U0010FFFF'):
1419-
subclass = UnicodeSubclass(text)
1419+
subclass = StrSubclass(text)
14201420
self.assertEqual(str(subclass), text)
14211421
self.assertEqual(len(subclass), len(text))
14221422
if text == 'ascii':
@@ -2169,6 +2169,9 @@ def __str__(self):
21692169
s = str(StrSubclassToStrSubclass("foo"))
21702170
self.assertEqual(s, "foofoo")
21712171
self.assertIs(type(s), StrSubclassToStrSubclass)
2172+
s = StrSubclass(StrSubclassToStrSubclass("foo"))
2173+
self.assertEqual(s, "foofoo")
2174+
self.assertIs(type(s), StrSubclass)
21722175

21732176
def test_unicode_repr(self):
21742177
class s1:

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Release date: tba
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #24731: Fixed crash on converting objects with special methods
14+
__bytes__, __trunc__, and __float__ returning instances of subclasses of
15+
bytes, int, and float to subclasses of bytes, int, and float correspondingly.
16+
1317
- Issue #25388: Fixed tokenizer crash when processing undecodable source code
1418
with a null byte.
1519

Objects/bytesobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,7 +2445,7 @@ bytes_methods[] = {
24452445
};
24462446

24472447
static PyObject *
2448-
str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
2448+
bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
24492449

24502450
static PyObject *
24512451
bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@@ -2460,7 +2460,7 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
24602460
_Py_IDENTIFIER(__bytes__);
24612461

24622462
if (type != &PyBytes_Type)
2463-
return str_subtype_new(type, args, kwds);
2463+
return bytes_subtype_new(type, args, kwds);
24642464
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, &x,
24652465
&encoding, &errors))
24662466
return NULL;
@@ -2687,7 +2687,7 @@ PyBytes_FromObject(PyObject *x)
26872687
}
26882688

26892689
static PyObject *
2690-
str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
2690+
bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
26912691
{
26922692
PyObject *tmp, *pnew;
26932693
Py_ssize_t n;
@@ -2696,7 +2696,7 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
26962696
tmp = bytes_new(&PyBytes_Type, args, kwds);
26972697
if (tmp == NULL)
26982698
return NULL;
2699-
assert(PyBytes_CheckExact(tmp));
2699+
assert(PyBytes_Check(tmp));
27002700
n = PyBytes_GET_SIZE(tmp);
27012701
pnew = type->tp_alloc(type, n);
27022702
if (pnew != NULL) {

Objects/floatobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@ float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
15671567
tmp = float_new(&PyFloat_Type, args, kwds);
15681568
if (tmp == NULL)
15691569
return NULL;
1570-
assert(PyFloat_CheckExact(tmp));
1570+
assert(PyFloat_Check(tmp));
15711571
newobj = type->tp_alloc(type, 0);
15721572
if (newobj == NULL) {
15731573
Py_DECREF(tmp);

Objects/longobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4405,7 +4405,7 @@ long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
44054405
tmp = (PyLongObject *)long_new(&PyLong_Type, args, kwds);
44064406
if (tmp == NULL)
44074407
return NULL;
4408-
assert(PyLong_CheckExact(tmp));
4408+
assert(PyLong_Check(tmp));
44094409
n = Py_SIZE(tmp);
44104410
if (n < 0)
44114411
n = -n;

0 commit comments

Comments
 (0)