summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2016-06-03 18:42:55 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2016-06-03 18:42:55 (GMT)
commit16931c3559689cfa9d98e3b56e612b7e1e635b7a (patch)
treedd63cb2a5960a03eeb2a9560222ffebe03965817
parentbb7f7327fea85d73e13950e83e5e39d57f7488df (diff)
downloadcpython-16931c3559689cfa9d98e3b56e612b7e1e635b7a.zip
cpython-16931c3559689cfa9d98e3b56e612b7e1e635b7a.tar.gz
cpython-16931c3559689cfa9d98e3b56e612b7e1e635b7a.tar.bz2
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.
-rw-r--r--Lib/test/test_float.py21
-rw-r--r--Lib/test/test_getargs2.py6
-rw-r--r--Misc/NEWS5
-rw-r--r--Objects/abstract.c30
-rw-r--r--Objects/floatobject.c46
5 files changed, 76 insertions, 32 deletions
diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py
index 427f044..68b212e 100644
--- a/Lib/test/test_float.py
+++ b/Lib/test/test_float.py
@@ -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())
diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py
index 8a51e0b..ecc1908 100644
--- a/Lib/test/test_getargs2.py
+++ b/Lib/test/test_getargs2.py
@@ -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):
diff --git a/Misc/NEWS b/Misc/NEWS
index 57edb49..f5a44bd 100644
--- 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.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 3e1ff97..12dd6a1 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -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);
}
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 5b2742a..da600f4 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -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;
}