]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-41974: Remove complex.__float__, complex.__floordiv__, etc (GH-22593)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 9 Oct 2020 11:14:37 +0000 (14:14 +0300)
committerGitHub <noreply@github.com>
Fri, 9 Oct 2020 11:14:37 +0000 (14:14 +0300)
Remove complex special methods __int__, __float__, __floordiv__,
__mod__, __divmod__, __rfloordiv__, __rmod__ and __rdivmod__
which always raised a TypeError.

Doc/whatsnew/3.10.rst
Lib/test/test_complex.py
Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst [new file with mode: 0644]
Objects/abstract.c
Objects/bytesobject.c
Objects/complexobject.c
Objects/floatobject.c
Objects/unicodeobject.c

index 4ada4be3b667151bd0a6eb47243332b9dc53def8..7401ba722fb4f98f71d6d6c70af85e000ac9fdd8 100644 (file)
@@ -255,6 +255,12 @@ Deprecated
 Removed
 =======
 
+* Removed special methods ``__int__``, ``__float__``, ``__floordiv__``,
+  ``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and
+  ``__rdivmod__`` of the :class:`complex` class.  They always raised
+  a :exc:`TypeError`.
+  (Contributed by Serhiy Storchaka in :issue:`41974`.)
+
 * The ``ParserBase.error()`` method from the private and undocumented ``_markupbase``
   module has been removed.  :class:`html.parser.HTMLParser` is the only subclass of
   ``ParserBase`` and its ``error()`` implementation has already been removed in
index d1f241f7a60c9fb7d14346a8ff147ecb76313d9c..af39ee878dc913df11a572d60b3455215d25ec94 100644 (file)
@@ -11,6 +11,14 @@ INF = float("inf")
 NAN = float("nan")
 # These tests ensure that complex math does the right thing
 
+ZERO_DIVISION = (
+    (1+1j, 0+0j),
+    (1+1j, 0.0),
+    (1+1j, 0),
+    (1.0, 0+0j),
+    (1, 0+0j),
+)
+
 class ComplexTest(unittest.TestCase):
 
     def assertAlmostEqual(self, a, b):
@@ -99,20 +107,34 @@ class ComplexTest(unittest.TestCase):
             self.check_div(complex(random(), random()),
                            complex(random(), random()))
 
-        self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)
-        self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
-
         self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j)
-        self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)
 
         for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]:
             z = complex(0, 0) / complex(denom_real, denom_imag)
             self.assertTrue(isnan(z.real))
             self.assertTrue(isnan(z.imag))
 
+    def test_truediv_zero_division(self):
+        for a, b in ZERO_DIVISION:
+            with self.assertRaises(ZeroDivisionError):
+                a / b
+
     def test_floordiv(self):
-        self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 1.5+0j)
-        self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j)
+        with self.assertRaises(TypeError):
+            (1+1j) // (1+0j)
+        with self.assertRaises(TypeError):
+            (1+1j) // 1.0
+        with self.assertRaises(TypeError):
+            (1+1j) // 1
+        with self.assertRaises(TypeError):
+            1.0 // (1+0j)
+        with self.assertRaises(TypeError):
+            1 // (1+0j)
+
+    def test_floordiv_zero_division(self):
+        for a, b in ZERO_DIVISION:
+            with self.assertRaises(TypeError):
+                a // b
 
     def test_richcompare(self):
         self.assertIs(complex.__eq__(1+1j, 1<<10000), False)
@@ -159,13 +181,32 @@ class ComplexTest(unittest.TestCase):
 
     def test_mod(self):
         # % is no longer supported on complex numbers
-        self.assertRaises(TypeError, (1+1j).__mod__, 0+0j)
-        self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0)
-        self.assertRaises(TypeError, (1+1j).__mod__, 4.3j)
+        with self.assertRaises(TypeError):
+            (1+1j) % (1+0j)
+        with self.assertRaises(TypeError):
+            (1+1j) % 1.0
+        with self.assertRaises(TypeError):
+            (1+1j) % 1
+        with self.assertRaises(TypeError):
+            1.0 % (1+0j)
+        with self.assertRaises(TypeError):
+            1 % (1+0j)
+
+    def test_mod_zero_division(self):
+        for a, b in ZERO_DIVISION:
+            with self.assertRaises(TypeError):
+                a % b
 
     def test_divmod(self):
         self.assertRaises(TypeError, divmod, 1+1j, 1+0j)
-        self.assertRaises(TypeError, divmod, 1+1j, 0+0j)
+        self.assertRaises(TypeError, divmod, 1+1j, 1.0)
+        self.assertRaises(TypeError, divmod, 1+1j, 1)
+        self.assertRaises(TypeError, divmod, 1.0, 1+0j)
+        self.assertRaises(TypeError, divmod, 1, 1+0j)
+
+    def test_divmod_zero_division(self):
+        for a, b in ZERO_DIVISION:
+            self.assertRaises(TypeError, divmod, a, b)
 
     def test_pow(self):
         self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0)
@@ -174,6 +215,7 @@ class ComplexTest(unittest.TestCase):
         self.assertAlmostEqual(pow(1j, -1), 1/1j)
         self.assertAlmostEqual(pow(1j, 200), 1)
         self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j)
+        self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
 
         a = 3.33+4.43j
         self.assertEqual(a ** 0j, 1)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst b/Misc/NEWS.d/next/Core and Builtins/2020-10-08-09-58-19.bpo-41974.8B-q8O.rst
new file mode 100644 (file)
index 0000000..034cfed
--- /dev/null
@@ -0,0 +1,4 @@
+Removed special methods ``__int__``, ``__float__``, ``__floordiv__``,
+``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and
+``__rdivmod__`` of the :class:`complex` class.  They always raised
+a :exc:`TypeError`.
index c30fb4eb6a604e2047074b3b900b81878e7baa3a..2ab3371a3f3cb9a1da6ca6c494f5e1d9b8e188fb 100644 (file)
@@ -747,10 +747,10 @@ done:
 int
 PyNumber_Check(PyObject *o)
 {
-    return o && Py_TYPE(o)->tp_as_number &&
-           (Py_TYPE(o)->tp_as_number->nb_index ||
-            Py_TYPE(o)->tp_as_number->nb_int ||
-            Py_TYPE(o)->tp_as_number->nb_float);
+    if (o == NULL)
+        return 0;
+    PyNumberMethods *nb = Py_TYPE(o)->tp_as_number;
+    return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o));
 }
 
 /* Binary operators */
@@ -1461,7 +1461,7 @@ PyNumber_Long(PyObject *o)
     }
 
     return type_error("int() argument must be a string, a bytes-like object "
-                      "or a number, not '%.200s'", o);
+                      "or a real number, not '%.200s'", o);
 }
 
 PyObject *
index 836a736037ba4388c7006b7fb12cf347a854cb24..990730cd8cdc106fcb21d33aa63cb21187058bfc 100644 (file)
@@ -522,7 +522,7 @@ formatlong(PyObject *v, int flags, int prec, int type)
     PyErr_Format(PyExc_TypeError,
         "%%%c format: %s is required, not %.200s", type,
         (type == 'o' || type == 'x' || type == 'X') ? "an integer"
-                                                    : "a number",
+                                                    : "a real number",
         Py_TYPE(v)->tp_name);
     return NULL;
 }
index 69f6c17b4a49cd08e0806e183012cbaec393fa6e..5ab839a9e9423a2a39dcf7fb30dec45d74142acf 100644 (file)
@@ -509,23 +509,6 @@ complex_div(PyObject *v, PyObject *w)
     return PyComplex_FromCComplex(quot);
 }
 
-static PyObject *
-complex_remainder(PyObject *v, PyObject *w)
-{
-    PyErr_SetString(PyExc_TypeError,
-                    "can't mod complex numbers.");
-    return NULL;
-}
-
-
-static PyObject *
-complex_divmod(PyObject *v, PyObject *w)
-{
-    PyErr_SetString(PyExc_TypeError,
-                    "can't take floor or mod of complex number.");
-    return NULL;
-}
-
 static PyObject *
 complex_pow(PyObject *v, PyObject *w, PyObject *z)
 {
@@ -562,14 +545,6 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z)
     return PyComplex_FromCComplex(p);
 }
 
-static PyObject *
-complex_int_div(PyObject *v, PyObject *w)
-{
-    PyErr_SetString(PyExc_TypeError,
-                    "can't take floor of complex number.");
-    return NULL;
-}
-
 static PyObject *
 complex_neg(PyComplexObject *v)
 {
@@ -668,22 +643,6 @@ Unimplemented:
     Py_RETURN_NOTIMPLEMENTED;
 }
 
-static PyObject *
-complex_int(PyObject *v)
-{
-    PyErr_SetString(PyExc_TypeError,
-               "can't convert complex to int");
-    return NULL;
-}
-
-static PyObject *
-complex_float(PyObject *v)
-{
-    PyErr_SetString(PyExc_TypeError,
-               "can't convert complex to float");
-    return NULL;
-}
-
 /*[clinic input]
 complex.conjugate
 
@@ -966,7 +925,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
     }
 
     nbr = Py_TYPE(r)->tp_as_number;
-    if (nbr == NULL || (nbr->nb_float == NULL && nbr->nb_index == NULL)) {
+    if (nbr == NULL ||
+        (nbr->nb_float == NULL && nbr->nb_index == NULL && !PyComplex_Check(r)))
+    {
         PyErr_Format(PyExc_TypeError,
                      "complex() first argument must be a string or a number, "
                      "not '%.200s'",
@@ -978,7 +939,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
     }
     if (i != NULL) {
         nbi = Py_TYPE(i)->tp_as_number;
-        if (nbi == NULL || (nbi->nb_float == NULL && nbi->nb_index == NULL)) {
+        if (nbi == NULL ||
+            (nbi->nb_float == NULL && nbi->nb_index == NULL && !PyComplex_Check(i)))
+        {
             PyErr_Format(PyExc_TypeError,
                          "complex() second argument must be a number, "
                          "not '%.200s'",
@@ -1057,8 +1020,8 @@ static PyNumberMethods complex_as_number = {
     (binaryfunc)complex_add,                    /* nb_add */
     (binaryfunc)complex_sub,                    /* nb_subtract */
     (binaryfunc)complex_mul,                    /* nb_multiply */
-    (binaryfunc)complex_remainder,              /* nb_remainder */
-    (binaryfunc)complex_divmod,                 /* nb_divmod */
+    0,                                          /* nb_remainder */
+    0,                                          /* nb_divmod */
     (ternaryfunc)complex_pow,                   /* nb_power */
     (unaryfunc)complex_neg,                     /* nb_negative */
     (unaryfunc)complex_pos,                     /* nb_positive */
@@ -1070,9 +1033,9 @@ static PyNumberMethods complex_as_number = {
     0,                                          /* nb_and */
     0,                                          /* nb_xor */
     0,                                          /* nb_or */
-    complex_int,                                /* nb_int */
+    0,                                          /* nb_int */
     0,                                          /* nb_reserved */
-    complex_float,                              /* nb_float */
+    0,                                          /* nb_float */
     0,                                          /* nb_inplace_add */
     0,                                          /* nb_inplace_subtract */
     0,                                          /* nb_inplace_multiply*/
@@ -1083,7 +1046,7 @@ static PyNumberMethods complex_as_number = {
     0,                                          /* nb_inplace_and */
     0,                                          /* nb_inplace_xor */
     0,                                          /* nb_inplace_or */
-    (binaryfunc)complex_int_div,                /* nb_floor_divide */
+    0,                                          /* nb_floor_divide */
     (binaryfunc)complex_div,                    /* nb_true_divide */
     0,                                          /* nb_inplace_floor_divide */
     0,                                          /* nb_inplace_true_divide */
index d0af0ea1a9825744fb902567b5e45822d907160d..828bde18df70ca14cb58c5aa0001a1ab6b43e991 100644 (file)
@@ -215,7 +215,7 @@ PyFloat_FromString(PyObject *v)
     }
     else {
         PyErr_Format(PyExc_TypeError,
-            "float() argument must be a string or a number, not '%.200s'",
+            "float() argument must be a string or a real number, not '%.200s'",
             Py_TYPE(v)->tp_name);
         return NULL;
     }
index 6ae06a508c6140a61d4a8ac2fb5619aad7cafa50..01e5c728b383fb45b72dc8752ea5bc1b3c4b8424 100644 (file)
@@ -14839,7 +14839,7 @@ wrongtype:
             break;
         default:
             PyErr_Format(PyExc_TypeError,
-                    "%%%c format: a number is required, "
+                    "%%%c format: a real number is required, "
                     "not %.200s",
                     type, Py_TYPE(v)->tp_name);
             break;