mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									a49de6be36
								
							
						
					
					
						commit
						15095800a3
					
				
					 8 changed files with 50 additions and 10 deletions
				
			
		|  | @ -744,6 +744,14 @@ def __bytes__(self): | ||||||
|             def __index__(self): |             def __index__(self): | ||||||
|                 return 42 |                 return 42 | ||||||
|         self.assertEqual(bytes(A()), b'a') |         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() |     # Test PyBytes_FromFormat() | ||||||
|     def test_from_format(self): |     def test_from_format(self): | ||||||
|  | @ -1465,6 +1473,9 @@ class ByteArraySubclass(bytearray): | ||||||
| class BytesSubclass(bytes): | class BytesSubclass(bytes): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | class OtherBytesSubclass(bytes): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class ByteArraySubclassTest(SubclassTest, unittest.TestCase): | class ByteArraySubclassTest(SubclassTest, unittest.TestCase): | ||||||
|     type2test = bytearray |     type2test = bytearray | ||||||
|     subclass2test = ByteArraySubclass |     subclass2test = ByteArraySubclass | ||||||
|  |  | ||||||
|  | @ -25,6 +25,12 @@ | ||||||
| test_dir = os.path.dirname(__file__) or os.curdir | test_dir = os.path.dirname(__file__) or os.curdir | ||||||
| format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt') | format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt') | ||||||
| 
 | 
 | ||||||
|  | class FloatSubclass(float): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class OtherFloatSubclass(float): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class GeneralFloatCases(unittest.TestCase): | class GeneralFloatCases(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_float(self): |     def test_float(self): | ||||||
|  | @ -167,6 +173,15 @@ def __float__(self): | ||||||
|                 return "" |                 return "" | ||||||
|         self.assertRaises(TypeError, time.sleep, Foo5()) |         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): |     def test_is_integer(self): | ||||||
|         self.assertFalse((1.1).is_integer()) |         self.assertFalse((1.1).is_integer()) | ||||||
|         self.assertTrue((1.).is_integer()) |         self.assertTrue((1.).is_integer()) | ||||||
|  |  | ||||||
|  | @ -24,6 +24,9 @@ | ||||||
|         ("\u0200", ValueError) |         ("\u0200", ValueError) | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | class IntSubclass(int): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class IntTestCases(unittest.TestCase): | class IntTestCases(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_basic(self): |     def test_basic(self): | ||||||
|  | @ -441,6 +444,10 @@ def __trunc__(self): | ||||||
|         good_int = TruncReturnsIntSubclass() |         good_int = TruncReturnsIntSubclass() | ||||||
|         n = int(good_int) |         n = int(good_int) | ||||||
|         self.assertEqual(n, 1) |         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 test_error_message(self): | ||||||
|         def check(s, base=None): |         def check(s, base=None): | ||||||
|  |  | ||||||
|  | @ -42,6 +42,9 @@ def duplicate_string(text): | ||||||
|     """ |     """ | ||||||
|     return text.encode().decode() |     return text.encode().decode() | ||||||
| 
 | 
 | ||||||
|  | class StrSubclass(str): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class UnicodeTest(string_tests.CommonTest, | class UnicodeTest(string_tests.CommonTest, | ||||||
|         string_tests.MixinStrUnicodeUserStringTest, |         string_tests.MixinStrUnicodeUserStringTest, | ||||||
|         string_tests.MixinStrUnicodeTest, |         string_tests.MixinStrUnicodeTest, | ||||||
|  | @ -1412,11 +1415,8 @@ def test_constructor(self): | ||||||
|             'unicode remains unicode' |             'unicode remains unicode' | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         class UnicodeSubclass(str): |  | ||||||
|             pass |  | ||||||
| 
 |  | ||||||
|         for text in ('ascii', '\xe9', '\u20ac', '\U0010FFFF'): |         for text in ('ascii', '\xe9', '\u20ac', '\U0010FFFF'): | ||||||
|             subclass = UnicodeSubclass(text) |             subclass = StrSubclass(text) | ||||||
|             self.assertEqual(str(subclass), text) |             self.assertEqual(str(subclass), text) | ||||||
|             self.assertEqual(len(subclass), len(text)) |             self.assertEqual(len(subclass), len(text)) | ||||||
|             if text == 'ascii': |             if text == 'ascii': | ||||||
|  | @ -2169,6 +2169,9 @@ def __str__(self): | ||||||
|         s = str(StrSubclassToStrSubclass("foo")) |         s = str(StrSubclassToStrSubclass("foo")) | ||||||
|         self.assertEqual(s, "foofoo") |         self.assertEqual(s, "foofoo") | ||||||
|         self.assertIs(type(s), StrSubclassToStrSubclass) |         self.assertIs(type(s), StrSubclassToStrSubclass) | ||||||
|  |         s = StrSubclass(StrSubclassToStrSubclass("foo")) | ||||||
|  |         self.assertEqual(s, "foofoo") | ||||||
|  |         self.assertIs(type(s), StrSubclass) | ||||||
| 
 | 
 | ||||||
|     def test_unicode_repr(self): |     def test_unicode_repr(self): | ||||||
|         class s1: |         class s1: | ||||||
|  |  | ||||||
|  | @ -10,6 +10,10 @@ Release date: tba | ||||||
| Core and Builtins | 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. | ||||||
|  | 
 | ||||||
| - Issue #25388: Fixed tokenizer crash when processing undecodable source code | - Issue #25388: Fixed tokenizer crash when processing undecodable source code | ||||||
|   with a null byte. |   with a null byte. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2445,7 +2445,7 @@ bytes_methods[] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); | bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|  | @ -2460,7 +2460,7 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|     _Py_IDENTIFIER(__bytes__); |     _Py_IDENTIFIER(__bytes__); | ||||||
| 
 | 
 | ||||||
|     if (type != &PyBytes_Type) |     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, |     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, &x, | ||||||
|                                      &encoding, &errors)) |                                      &encoding, &errors)) | ||||||
|         return NULL; |         return NULL; | ||||||
|  | @ -2687,7 +2687,7 @@ PyBytes_FromObject(PyObject *x) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | bytes_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
| { | { | ||||||
|     PyObject *tmp, *pnew; |     PyObject *tmp, *pnew; | ||||||
|     Py_ssize_t n; |     Py_ssize_t n; | ||||||
|  | @ -2696,7 +2696,7 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|     tmp = bytes_new(&PyBytes_Type, args, kwds); |     tmp = bytes_new(&PyBytes_Type, args, kwds); | ||||||
|     if (tmp == NULL) |     if (tmp == NULL) | ||||||
|         return NULL; |         return NULL; | ||||||
|     assert(PyBytes_CheckExact(tmp)); |     assert(PyBytes_Check(tmp)); | ||||||
|     n = PyBytes_GET_SIZE(tmp); |     n = PyBytes_GET_SIZE(tmp); | ||||||
|     pnew = type->tp_alloc(type, n); |     pnew = type->tp_alloc(type, n); | ||||||
|     if (pnew != NULL) { |     if (pnew != NULL) { | ||||||
|  |  | ||||||
|  | @ -1567,7 +1567,7 @@ float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|     tmp = float_new(&PyFloat_Type, args, kwds); |     tmp = float_new(&PyFloat_Type, args, kwds); | ||||||
|     if (tmp == NULL) |     if (tmp == NULL) | ||||||
|         return NULL; |         return NULL; | ||||||
|     assert(PyFloat_CheckExact(tmp)); |     assert(PyFloat_Check(tmp)); | ||||||
|     newobj = type->tp_alloc(type, 0); |     newobj = type->tp_alloc(type, 0); | ||||||
|     if (newobj == NULL) { |     if (newobj == NULL) { | ||||||
|         Py_DECREF(tmp); |         Py_DECREF(tmp); | ||||||
|  |  | ||||||
|  | @ -4405,7 +4405,7 @@ long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
|     tmp = (PyLongObject *)long_new(&PyLong_Type, args, kwds); |     tmp = (PyLongObject *)long_new(&PyLong_Type, args, kwds); | ||||||
|     if (tmp == NULL) |     if (tmp == NULL) | ||||||
|         return NULL; |         return NULL; | ||||||
|     assert(PyLong_CheckExact(tmp)); |     assert(PyLong_Check(tmp)); | ||||||
|     n = Py_SIZE(tmp); |     n = Py_SIZE(tmp); | ||||||
|     if (n < 0) |     if (n < 0) | ||||||
|         n = -n; |         n = -n; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka