]> granicus.if.org Git - python/commitdiff
Issue #26983: float() now always return an instance of exact float.
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 3 Jun 2016 18:42:55 +0000 (21:42 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Fri, 3 Jun 2016 18:42:55 +0000 (21:42 +0300)
The deprecation warning is emitted if __float__ returns an instance of
a strict subclass of float.  In a future versions of Python this can
be an error.

Lib/test/test_float.py
Lib/test/test_getargs2.py
Misc/NEWS
Objects/abstract.c
Objects/floatobject.c

index 427f044af45732701850b4d81866c57a699c726f..68b212e1959e8b21d11ba62303b255d2d2b4b09a 100644 (file)
@@ -161,11 +161,12 @@ class GeneralFloatCases(unittest.TestCase):
             def __float__(self):
                 return float(str(self)) + 1
 
-        self.assertAlmostEqual(float(Foo1()), 42.)
-        self.assertAlmostEqual(float(Foo2()), 42.)
-        self.assertAlmostEqual(float(Foo3(21)), 42.)
+        self.assertEqual(float(Foo1()), 42.)
+        self.assertEqual(float(Foo2()), 42.)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(float(Foo3(21)), 42.)
         self.assertRaises(TypeError, float, Foo4(42))
-        self.assertAlmostEqual(float(FooStr('8')), 9.)
+        self.assertEqual(float(FooStr('8')), 9.)
 
         class Foo5:
             def __float__(self):
@@ -176,10 +177,14 @@ class GeneralFloatCases(unittest.TestCase):
         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)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(float(F()), 42.)
+        with self.assertWarns(DeprecationWarning):
+            self.assertIs(type(float(F())), float)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(FloatSubclass(F()), 42.)
+        with self.assertWarns(DeprecationWarning):
+            self.assertIs(type(FloatSubclass(F())), FloatSubclass)
 
     def test_is_integer(self):
         self.assertFalse((1.1).is_integer())
index 8a51e0bf8c420511bf52ca4d908b3ae1d99203ff..ecc19088ff625851a136fb8432df3a1ff189f5e8 100644 (file)
@@ -365,7 +365,8 @@ class Float_TestCase(unittest.TestCase):
         self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5)
         self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5)
         self.assertRaises(TypeError, getargs_f, BadFloat())
-        self.assertEqual(getargs_f(BadFloat2()), 4.25)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(getargs_f(BadFloat2()), 4.25)
         self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5)
 
         for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF):
@@ -390,7 +391,8 @@ class Float_TestCase(unittest.TestCase):
         self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5)
         self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5)
         self.assertRaises(TypeError, getargs_d, BadFloat())
-        self.assertEqual(getargs_d(BadFloat2()), 4.25)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(getargs_d(BadFloat2()), 4.25)
         self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5)
 
         for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF):
index 57edb4963059858a5b601ac10c26af8607be0486..f5a44bd5f9a1417bd8d9eca655edeb1dd6b990d2 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,11 @@ What's New in Python 3.6.0 alpha 2
 Core and Builtins
 -----------------
 
+- Issue #26983: float() now always return an instance of exact float.
+  The deprecation warning is emitted if __float__ returns an instance of
+  a strict subclass of float.  In a future versions of Python this can
+  be an error.
+
 - Issue #27097: Python interpreter is now about 7% faster due to optimized
   instruction decoding.  Based on patch by Demur Rumed.
 
index 3e1ff97547e145d286b71e51309138b88ac6f904..12dd6a16ce157ae263230238b553a572089da1ec 100644 (file)
@@ -1351,21 +1351,39 @@ PyNumber_Float(PyObject *o)
 
     if (o == NULL)
         return null_error();
+    if (PyFloat_CheckExact(o)) {
+        Py_INCREF(o);
+        return o;
+    }
     m = o->ob_type->tp_as_number;
     if (m && m->nb_float) { /* This should include subclasses of float */
         PyObject *res = m->nb_float(o);
-        if (res && !PyFloat_Check(res)) {
+        double val;
+        if (!res || PyFloat_CheckExact(res)) {
+            return res;
+        }
+        if (!PyFloat_Check(res)) {
             PyErr_Format(PyExc_TypeError,
-              "__float__ returned non-float (type %.200s)",
-              res->ob_type->tp_name);
+                         "%.50s.__float__ returned non-float (type %.50s)",
+                         o->ob_type->tp_name, res->ob_type->tp_name);
             Py_DECREF(res);
             return NULL;
         }
-        return res;
+        /* Issue #26983: warn if 'res' not of exact type float. */
+        if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
+                "%.50s.__float__ returned non-float (type %.50s).  "
+                "The ability to return an instance of a strict subclass of float "
+                "is deprecated, and may be removed in a future version of Python.",
+                o->ob_type->tp_name, res->ob_type->tp_name)) {
+            Py_DECREF(res);
+            return NULL;
+        }
+        val = PyFloat_AS_DOUBLE(res);
+        Py_DECREF(res);
+        return PyFloat_FromDouble(val);
     }
     if (PyFloat_Check(o)) { /* A float subclass with nb_float == NULL */
-        PyFloatObject *po = (PyFloatObject *)o;
-        return PyFloat_FromDouble(po->ob_fval);
+        return PyFloat_FromDouble(PyFloat_AS_DOUBLE(o));
     }
     return PyFloat_FromString(o);
 }
index 5b2742a6c853e1bd6c0903ecc698c674e4099d78..da600f4aa829d2059cf8b2b84380612c01613108 100644 (file)
@@ -215,35 +215,49 @@ double
 PyFloat_AsDouble(PyObject *op)
 {
     PyNumberMethods *nb;
-    PyFloatObject *fo;
+    PyObject *res;
     double val;
 
-    if (op && PyFloat_Check(op))
-        return PyFloat_AS_DOUBLE((PyFloatObject*) op);
-
     if (op == NULL) {
         PyErr_BadArgument();
         return -1;
     }
 
-    if ((nb = Py_TYPE(op)->tp_as_number) == NULL || nb->nb_float == NULL) {
-        PyErr_SetString(PyExc_TypeError, "a float is required");
-        return -1;
+    if (PyFloat_Check(op)) {
+        return PyFloat_AS_DOUBLE(op);
     }
 
-    fo = (PyFloatObject*) (*nb->nb_float) (op);
-    if (fo == NULL)
-        return -1;
-    if (!PyFloat_Check(fo)) {
-        Py_DECREF(fo);
-        PyErr_SetString(PyExc_TypeError,
-                        "nb_float should return float object");
+    nb = Py_TYPE(op)->tp_as_number;
+    if (nb == NULL || nb->nb_float == NULL) {
+        PyErr_Format(PyExc_TypeError, "must be real number, not %.50s",
+                     op->ob_type->tp_name);
         return -1;
     }
 
-    val = PyFloat_AS_DOUBLE(fo);
-    Py_DECREF(fo);
+    res = (*nb->nb_float) (op);
+    if (res == NULL) {
+        return -1;
+    }
+    if (!PyFloat_CheckExact(res)) {
+        if (!PyFloat_Check(res)) {
+            PyErr_Format(PyExc_TypeError,
+                         "%.50s.__float__ returned non-float (type %.50s)",
+                         op->ob_type->tp_name, res->ob_type->tp_name);
+            Py_DECREF(res);
+            return -1;
+        }
+        if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
+                "%.50s.__float__ returned non-float (type %.50s).  "
+                "The ability to return an instance of a strict subclass of float "
+                "is deprecated, and may be removed in a future version of Python.",
+                op->ob_type->tp_name, res->ob_type->tp_name)) {
+            Py_DECREF(res);
+            return -1;
+        }
+    }
 
+    val = PyFloat_AS_DOUBLE(res);
+    Py_DECREF(res);
     return val;
 }