]> granicus.if.org Git - python/commitdiff
Issue #24731: Fixed crash on converting objects with special methods
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 25 Nov 2015 13:55:54 +0000 (15:55 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Wed, 25 Nov 2015 13:55:54 +0000 (15:55 +0200)
__str__, __trunc__, and __float__ returning instances of subclasses of
str, long, and float to subclasses of str, long, and float correspondingly.

Lib/test/test_float.py
Lib/test/test_int.py
Lib/test/test_long.py
Lib/test/test_str.py
Lib/test/test_unicode.py
Misc/NEWS
Objects/floatobject.c
Objects/longobject.c
Objects/stringobject.c

index 422430601ecba635b4c034f6de394b52cbe93416..4ec894cd45bc636a6c2505b80ef6fb65d9c08c2a 100644 (file)
@@ -27,6 +27,12 @@ requires_IEEE_754 = unittest.skipUnless(have_getformat and
 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):
@@ -200,6 +206,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())
index 2ca6cf2f529f1af698ee048b68151e866bdf12a3..0dd8efc53f2b55f77e3849bc0687ea7cb1d3362b 100644 (file)
@@ -45,6 +45,9 @@ if have_unicode:
         (unichr(0x200), ValueError),
 ]
 
+class IntSubclass(int):
+    pass
+
 class IntLongCommonTests(object):
 
     """Mixin of test cases to share between both test_int and test_long."""
@@ -477,6 +480,18 @@ class IntTestCases(IntLongCommonTests, unittest.TestCase):
                     self.fail("Failed to raise TypeError with %s" %
                               ((base, trunc_result_base),))
 
+                class TruncReturnsIntSubclass(base):
+                    def __trunc__(self):
+                        return True
+                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_main():
     run_unittest(IntTestCases)
 
index ffa4774b1f9beffbc38b2730d27aa1b71e23dbf7..b65d24cd9418913a8f496a10cc1c34b284f9c413 100644 (file)
@@ -79,6 +79,12 @@ if test_support.have_unicode:
         (unichr(0x200), ValueError),
 ]
 
+class LongSubclass(long):
+    pass
+
+class OtherLongSubclass(long):
+    pass
+
 class LongTest(test_int.IntLongCommonTests, unittest.TestCase):
 
     ntype = long
@@ -539,6 +545,17 @@ class LongTest(test_int.IntLongCommonTests, unittest.TestCase):
                     self.fail("Failed to raise TypeError with %s" %
                               ((base, trunc_result_base),))
 
+                class TruncReturnsLongSubclass(base):
+                    def __long__(self):
+                        return OtherLongSubclass(42L)
+                good_int = TruncReturnsLongSubclass()
+                n = long(good_int)
+                self.assertEqual(n, 42L)
+                self.assertIs(type(n), OtherLongSubclass)
+                n = LongSubclass(good_int)
+                self.assertEqual(n, 42L)
+                self.assertIs(type(n), LongSubclass)
+
     def test_misc(self):
 
         # check the extremes in int<->long conversion
index 774c6346ffb0833910c33d839622909a4ca6d0e9..5bb9f4867bcb0e888910c5eedfc0849840d3557f 100644 (file)
@@ -4,6 +4,9 @@ import sys
 from test import test_support, string_tests
 
 
+class StrSubclass(str):
+    pass
+
 class StrTest(
     string_tests.CommonTest,
     string_tests.MixinStrUnicodeUserStringTest,
@@ -107,6 +110,9 @@ class StrTest(
         self.assertEqual(str(Foo6("bar")), "foos")
         self.assertEqual(str(Foo7("bar")), "foos")
         self.assertEqual(str(Foo8("foo")), "foofoo")
+        self.assertIs(type(str(Foo8("foo"))), Foo8)
+        self.assertEqual(StrSubclass(Foo8("foo")), "foofoo")
+        self.assertIs(type(StrSubclass(Foo8("foo"))), StrSubclass)
         self.assertEqual(str(Foo9("foo")), "string")
         self.assertEqual(unicode(Foo9("foo")), u"not unicode")
 
index be8f89be07ce4d70825af899b95932efabcb39be..b12f982e16f4d4a64209f4c8b83585c9d7afca9e 100644 (file)
@@ -33,6 +33,9 @@ def search_function(encoding):
         return None
 codecs.register(search_function)
 
+class UnicodeSubclass(unicode):
+    pass
+
 class UnicodeTest(
     string_tests.CommonTest,
     string_tests.MixinStrUnicodeUserStringTest,
@@ -685,9 +688,6 @@ class UnicodeTest(
             u'unicode remains unicode'
         )
 
-        class UnicodeSubclass(unicode):
-            pass
-
         self.assertEqual(
             unicode(UnicodeSubclass('unicode subclass becomes unicode')),
             u'unicode subclass becomes unicode'
@@ -1269,6 +1269,9 @@ class UnicodeTest(
         self.assertEqual(unicode(Foo6("bar")), u"foou")
         self.assertEqual(unicode(Foo7("bar")), u"foou")
         self.assertEqual(unicode(Foo8("foo")), u"foofoo")
+        self.assertIs(type(unicode(Foo8("foo"))), Foo8)
+        self.assertEqual(UnicodeSubclass(Foo8("foo")), u"foofoo")
+        self.assertIs(type(UnicodeSubclass(Foo8("foo"))), UnicodeSubclass)
         self.assertEqual(str(Foo9("foo")), "string")
         self.assertEqual(unicode(Foo9("foo")), u"not unicode")
 
index 4c1dc1a706d20fccda4c2f1b27fa32fbd4c63ff1..341264bf1ccd38a22e396dd0871d9ef212bb0cb9 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,10 @@ What's New in Python 2.7.12?
 Core and Builtins
 -----------------
 
+- Issue #24731: Fixed crash on converting objects with special methods
+  __str__, __trunc__, and __float__ returning instances of subclasses of
+  str, long, and float to subclasses of str, long, and float correspondingly.
+
 Library
 -------
 
index 1143fab1420ad31fa28a74691d77b9a32c519bc3..82f996067b5eccca8b7006af1fd055a65e86b171 100644 (file)
@@ -1839,7 +1839,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);
index 405be2e045a62a713c826811ee2288676848d32d..6427f425f68b86ca1767e70626eb71547424c41c 100644 (file)
@@ -4068,7 +4068,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;
index c1e12a7aaea3fa125e0684893e83666e58a25ead..14c51771c33ce299b6d0a64b63a8af95a1fd736d 100644 (file)
@@ -3719,7 +3719,7 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     tmp = string_new(&PyString_Type, args, kwds);
     if (tmp == NULL)
         return NULL;
-    assert(PyString_CheckExact(tmp));
+    assert(PyString_Check(tmp));
     n = PyString_GET_SIZE(tmp);
     pnew = type->tp_alloc(type, n);
     if (pnew != NULL) {