mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
bb7f7327fe
commit
16931c3559
|
@ -161,11 +161,12 @@ class GeneralFloatCases(unittest.TestCase):
|
||||||
def __float__(self):
|
def __float__(self):
|
||||||
return float(str(self)) + 1
|
return float(str(self)) + 1
|
||||||
|
|
||||||
self.assertAlmostEqual(float(Foo1()), 42.)
|
self.assertEqual(float(Foo1()), 42.)
|
||||||
self.assertAlmostEqual(float(Foo2()), 42.)
|
self.assertEqual(float(Foo2()), 42.)
|
||||||
self.assertAlmostEqual(float(Foo3(21)), 42.)
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
self.assertEqual(float(Foo3(21)), 42.)
|
||||||
self.assertRaises(TypeError, float, Foo4(42))
|
self.assertRaises(TypeError, float, Foo4(42))
|
||||||
self.assertAlmostEqual(float(FooStr('8')), 9.)
|
self.assertEqual(float(FooStr('8')), 9.)
|
||||||
|
|
||||||
class Foo5:
|
class Foo5:
|
||||||
def __float__(self):
|
def __float__(self):
|
||||||
|
@ -176,10 +177,14 @@ class GeneralFloatCases(unittest.TestCase):
|
||||||
class F:
|
class F:
|
||||||
def __float__(self):
|
def __float__(self):
|
||||||
return OtherFloatSubclass(42.)
|
return OtherFloatSubclass(42.)
|
||||||
self.assertAlmostEqual(float(F()), 42.)
|
with self.assertWarns(DeprecationWarning):
|
||||||
self.assertIs(type(float(F())), OtherFloatSubclass)
|
self.assertEqual(float(F()), 42.)
|
||||||
self.assertAlmostEqual(FloatSubclass(F()), 42.)
|
with self.assertWarns(DeprecationWarning):
|
||||||
self.assertIs(type(FloatSubclass(F())), FloatSubclass)
|
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):
|
def test_is_integer(self):
|
||||||
self.assertFalse((1.1).is_integer())
|
self.assertFalse((1.1).is_integer())
|
||||||
|
|
|
@ -365,7 +365,8 @@ class Float_TestCase(unittest.TestCase):
|
||||||
self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5)
|
self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5)
|
||||||
self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5)
|
self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5)
|
||||||
self.assertRaises(TypeError, getargs_f, BadFloat())
|
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)
|
self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5)
|
||||||
|
|
||||||
for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF):
|
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(FloatSubclass(7.5)), 7.5)
|
||||||
self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5)
|
self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5)
|
||||||
self.assertRaises(TypeError, getargs_d, BadFloat())
|
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)
|
self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5)
|
||||||
|
|
||||||
for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF):
|
for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF):
|
||||||
|
|
|
@ -10,6 +10,11 @@ What's New in Python 3.6.0 alpha 2
|
||||||
Core and Builtins
|
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
|
- Issue #27097: Python interpreter is now about 7% faster due to optimized
|
||||||
instruction decoding. Based on patch by Demur Rumed.
|
instruction decoding. Based on patch by Demur Rumed.
|
||||||
|
|
||||||
|
|
|
@ -1351,21 +1351,39 @@ PyNumber_Float(PyObject *o)
|
||||||
|
|
||||||
if (o == NULL)
|
if (o == NULL)
|
||||||
return null_error();
|
return null_error();
|
||||||
|
if (PyFloat_CheckExact(o)) {
|
||||||
|
Py_INCREF(o);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
m = o->ob_type->tp_as_number;
|
m = o->ob_type->tp_as_number;
|
||||||
if (m && m->nb_float) { /* This should include subclasses of float */
|
if (m && m->nb_float) { /* This should include subclasses of float */
|
||||||
PyObject *res = m->nb_float(o);
|
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,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"__float__ returned non-float (type %.200s)",
|
"%.50s.__float__ returned non-float (type %.50s)",
|
||||||
res->ob_type->tp_name);
|
o->ob_type->tp_name, res->ob_type->tp_name);
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
return NULL;
|
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 */
|
if (PyFloat_Check(o)) { /* A float subclass with nb_float == NULL */
|
||||||
PyFloatObject *po = (PyFloatObject *)o;
|
return PyFloat_FromDouble(PyFloat_AS_DOUBLE(o));
|
||||||
return PyFloat_FromDouble(po->ob_fval);
|
|
||||||
}
|
}
|
||||||
return PyFloat_FromString(o);
|
return PyFloat_FromString(o);
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,35 +215,49 @@ double
|
||||||
PyFloat_AsDouble(PyObject *op)
|
PyFloat_AsDouble(PyObject *op)
|
||||||
{
|
{
|
||||||
PyNumberMethods *nb;
|
PyNumberMethods *nb;
|
||||||
PyFloatObject *fo;
|
PyObject *res;
|
||||||
double val;
|
double val;
|
||||||
|
|
||||||
if (op && PyFloat_Check(op))
|
|
||||||
return PyFloat_AS_DOUBLE((PyFloatObject*) op);
|
|
||||||
|
|
||||||
if (op == NULL) {
|
if (op == NULL) {
|
||||||
PyErr_BadArgument();
|
PyErr_BadArgument();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((nb = Py_TYPE(op)->tp_as_number) == NULL || nb->nb_float == NULL) {
|
if (PyFloat_Check(op)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "a float is required");
|
return PyFloat_AS_DOUBLE(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fo = (PyFloatObject*) (*nb->nb_float) (op);
|
res = (*nb->nb_float) (op);
|
||||||
if (fo == NULL)
|
if (res == NULL) {
|
||||||
return -1;
|
|
||||||
if (!PyFloat_Check(fo)) {
|
|
||||||
Py_DECREF(fo);
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"nb_float should return float object");
|
|
||||||
return -1;
|
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(fo);
|
val = PyFloat_AS_DOUBLE(res);
|
||||||
Py_DECREF(fo);
|
Py_DECREF(res);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue