Skip to content

Commit e2ec0b2

Browse files
bpo-41974: Remove complex.__float__, complex.__floordiv__, etc (GH-22593)
Remove complex special methods __int__, __float__, __floordiv__, __mod__, __divmod__, __rfloordiv__, __rmod__ and __rdivmod__ which always raised a TypeError.
1 parent 48f305f commit e2ec0b2

File tree

8 files changed

+81
-66
lines changed

8 files changed

+81
-66
lines changed

Doc/whatsnew/3.10.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@ Deprecated
255255
Removed
256256
=======
257257

258+
* Removed special methods ``__int__``, ``__float__``, ``__floordiv__``,
259+
``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and
260+
``__rdivmod__`` of the :class:`complex` class. They always raised
261+
a :exc:`TypeError`.
262+
(Contributed by Serhiy Storchaka in :issue:`41974`.)
263+
258264
* The ``ParserBase.error()`` method from the private and undocumented ``_markupbase``
259265
module has been removed. :class:`html.parser.HTMLParser` is the only subclass of
260266
``ParserBase`` and its ``error()`` implementation has already been removed in

Lib/test/test_complex.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
NAN = float("nan")
1212
# These tests ensure that complex math does the right thing
1313

14+
ZERO_DIVISION = (
15+
(1+1j, 0+0j),
16+
(1+1j, 0.0),
17+
(1+1j, 0),
18+
(1.0, 0+0j),
19+
(1, 0+0j),
20+
)
21+
1422
class ComplexTest(unittest.TestCase):
1523

1624
def assertAlmostEqual(self, a, b):
@@ -99,20 +107,34 @@ def test_truediv(self):
99107
self.check_div(complex(random(), random()),
100108
complex(random(), random()))
101109

102-
self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)
103-
self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
104-
105110
self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j)
106-
self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)
107111

108112
for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]:
109113
z = complex(0, 0) / complex(denom_real, denom_imag)
110114
self.assertTrue(isnan(z.real))
111115
self.assertTrue(isnan(z.imag))
112116

117+
def test_truediv_zero_division(self):
118+
for a, b in ZERO_DIVISION:
119+
with self.assertRaises(ZeroDivisionError):
120+
a / b
121+
113122
def test_floordiv(self):
114-
self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 1.5+0j)
115-
self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j)
123+
with self.assertRaises(TypeError):
124+
(1+1j) // (1+0j)
125+
with self.assertRaises(TypeError):
126+
(1+1j) // 1.0
127+
with self.assertRaises(TypeError):
128+
(1+1j) // 1
129+
with self.assertRaises(TypeError):
130+
1.0 // (1+0j)
131+
with self.assertRaises(TypeError):
132+
1 // (1+0j)
133+
134+
def test_floordiv_zero_division(self):
135+
for a, b in ZERO_DIVISION:
136+
with self.assertRaises(TypeError):
137+
a // b
116138

117139
def test_richcompare(self):
118140
self.assertIs(complex.__eq__(1+1j, 1<<10000), False)
@@ -159,13 +181,32 @@ def check(n, deltas, is_equal, imag = 0.0):
159181

160182
def test_mod(self):
161183
# % is no longer supported on complex numbers
162-
self.assertRaises(TypeError, (1+1j).__mod__, 0+0j)
163-
self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0)
164-
self.assertRaises(TypeError, (1+1j).__mod__, 4.3j)
184+
with self.assertRaises(TypeError):
185+
(1+1j) % (1+0j)
186+
with self.assertRaises(TypeError):
187+
(1+1j) % 1.0
188+
with self.assertRaises(TypeError):
189+
(1+1j) % 1
190+
with self.assertRaises(TypeError):
191+
1.0 % (1+0j)
192+
with self.assertRaises(TypeError):
193+
1 % (1+0j)
194+
195+
def test_mod_zero_division(self):
196+
for a, b in ZERO_DIVISION:
197+
with self.assertRaises(TypeError):
198+
a % b
165199

166200
def test_divmod(self):
167201
self.assertRaises(TypeError, divmod, 1+1j, 1+0j)
168-
self.assertRaises(TypeError, divmod, 1+1j, 0+0j)
202+
self.assertRaises(TypeError, divmod, 1+1j, 1.0)
203+
self.assertRaises(TypeError, divmod, 1+1j, 1)
204+
self.assertRaises(TypeError, divmod, 1.0, 1+0j)
205+
self.assertRaises(TypeError, divmod, 1, 1+0j)
206+
207+
def test_divmod_zero_division(self):
208+
for a, b in ZERO_DIVISION:
209+
self.assertRaises(TypeError, divmod, a, b)
169210

170211
def test_pow(self):
171212
self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0)
@@ -174,6 +215,7 @@ def test_pow(self):
174215
self.assertAlmostEqual(pow(1j, -1), 1/1j)
175216
self.assertAlmostEqual(pow(1j, 200), 1)
176217
self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j)
218+
self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
177219

178220
a = 3.33+4.43j
179221
self.assertEqual(a ** 0j, 1)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Removed special methods ``__int__``, ``__float__``, ``__floordiv__``,
2+
``__mod__``, ``__divmod__``, ``__rfloordiv__``, ``__rmod__`` and
3+
``__rdivmod__`` of the :class:`complex` class. They always raised
4+
a :exc:`TypeError`.

Objects/abstract.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -747,10 +747,10 @@ PyObject_Format(PyObject *obj, PyObject *format_spec)
747747
int
748748
PyNumber_Check(PyObject *o)
749749
{
750-
return o && Py_TYPE(o)->tp_as_number &&
751-
(Py_TYPE(o)->tp_as_number->nb_index ||
752-
Py_TYPE(o)->tp_as_number->nb_int ||
753-
Py_TYPE(o)->tp_as_number->nb_float);
750+
if (o == NULL)
751+
return 0;
752+
PyNumberMethods *nb = Py_TYPE(o)->tp_as_number;
753+
return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o));
754754
}
755755

756756
/* Binary operators */
@@ -1461,7 +1461,7 @@ PyNumber_Long(PyObject *o)
14611461
}
14621462

14631463
return type_error("int() argument must be a string, a bytes-like object "
1464-
"or a number, not '%.200s'", o);
1464+
"or a real number, not '%.200s'", o);
14651465
}
14661466

14671467
PyObject *

Objects/bytesobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ formatlong(PyObject *v, int flags, int prec, int type)
522522
PyErr_Format(PyExc_TypeError,
523523
"%%%c format: %s is required, not %.200s", type,
524524
(type == 'o' || type == 'x' || type == 'X') ? "an integer"
525-
: "a number",
525+
: "a real number",
526526
Py_TYPE(v)->tp_name);
527527
return NULL;
528528
}

Objects/complexobject.c

Lines changed: 11 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -509,23 +509,6 @@ complex_div(PyObject *v, PyObject *w)
509509
return PyComplex_FromCComplex(quot);
510510
}
511511

512-
static PyObject *
513-
complex_remainder(PyObject *v, PyObject *w)
514-
{
515-
PyErr_SetString(PyExc_TypeError,
516-
"can't mod complex numbers.");
517-
return NULL;
518-
}
519-
520-
521-
static PyObject *
522-
complex_divmod(PyObject *v, PyObject *w)
523-
{
524-
PyErr_SetString(PyExc_TypeError,
525-
"can't take floor or mod of complex number.");
526-
return NULL;
527-
}
528-
529512
static PyObject *
530513
complex_pow(PyObject *v, PyObject *w, PyObject *z)
531514
{
@@ -562,14 +545,6 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z)
562545
return PyComplex_FromCComplex(p);
563546
}
564547

565-
static PyObject *
566-
complex_int_div(PyObject *v, PyObject *w)
567-
{
568-
PyErr_SetString(PyExc_TypeError,
569-
"can't take floor of complex number.");
570-
return NULL;
571-
}
572-
573548
static PyObject *
574549
complex_neg(PyComplexObject *v)
575550
{
@@ -668,22 +643,6 @@ complex_richcompare(PyObject *v, PyObject *w, int op)
668643
Py_RETURN_NOTIMPLEMENTED;
669644
}
670645

671-
static PyObject *
672-
complex_int(PyObject *v)
673-
{
674-
PyErr_SetString(PyExc_TypeError,
675-
"can't convert complex to int");
676-
return NULL;
677-
}
678-
679-
static PyObject *
680-
complex_float(PyObject *v)
681-
{
682-
PyErr_SetString(PyExc_TypeError,
683-
"can't convert complex to float");
684-
return NULL;
685-
}
686-
687646
/*[clinic input]
688647
complex.conjugate
689648
@@ -966,7 +925,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
966925
}
967926

968927
nbr = Py_TYPE(r)->tp_as_number;
969-
if (nbr == NULL || (nbr->nb_float == NULL && nbr->nb_index == NULL)) {
928+
if (nbr == NULL ||
929+
(nbr->nb_float == NULL && nbr->nb_index == NULL && !PyComplex_Check(r)))
930+
{
970931
PyErr_Format(PyExc_TypeError,
971932
"complex() first argument must be a string or a number, "
972933
"not '%.200s'",
@@ -978,7 +939,9 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i)
978939
}
979940
if (i != NULL) {
980941
nbi = Py_TYPE(i)->tp_as_number;
981-
if (nbi == NULL || (nbi->nb_float == NULL && nbi->nb_index == NULL)) {
942+
if (nbi == NULL ||
943+
(nbi->nb_float == NULL && nbi->nb_index == NULL && !PyComplex_Check(i)))
944+
{
982945
PyErr_Format(PyExc_TypeError,
983946
"complex() second argument must be a number, "
984947
"not '%.200s'",
@@ -1057,8 +1020,8 @@ static PyNumberMethods complex_as_number = {
10571020
(binaryfunc)complex_add, /* nb_add */
10581021
(binaryfunc)complex_sub, /* nb_subtract */
10591022
(binaryfunc)complex_mul, /* nb_multiply */
1060-
(binaryfunc)complex_remainder, /* nb_remainder */
1061-
(binaryfunc)complex_divmod, /* nb_divmod */
1023+
0, /* nb_remainder */
1024+
0, /* nb_divmod */
10621025
(ternaryfunc)complex_pow, /* nb_power */
10631026
(unaryfunc)complex_neg, /* nb_negative */
10641027
(unaryfunc)complex_pos, /* nb_positive */
@@ -1070,9 +1033,9 @@ static PyNumberMethods complex_as_number = {
10701033
0, /* nb_and */
10711034
0, /* nb_xor */
10721035
0, /* nb_or */
1073-
complex_int, /* nb_int */
1036+
0, /* nb_int */
10741037
0, /* nb_reserved */
1075-
complex_float, /* nb_float */
1038+
0, /* nb_float */
10761039
0, /* nb_inplace_add */
10771040
0, /* nb_inplace_subtract */
10781041
0, /* nb_inplace_multiply*/
@@ -1083,7 +1046,7 @@ static PyNumberMethods complex_as_number = {
10831046
0, /* nb_inplace_and */
10841047
0, /* nb_inplace_xor */
10851048
0, /* nb_inplace_or */
1086-
(binaryfunc)complex_int_div, /* nb_floor_divide */
1049+
0, /* nb_floor_divide */
10871050
(binaryfunc)complex_div, /* nb_true_divide */
10881051
0, /* nb_inplace_floor_divide */
10891052
0, /* nb_inplace_true_divide */

Objects/floatobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ PyFloat_FromString(PyObject *v)
215215
}
216216
else {
217217
PyErr_Format(PyExc_TypeError,
218-
"float() argument must be a string or a number, not '%.200s'",
218+
"float() argument must be a string or a real number, not '%.200s'",
219219
Py_TYPE(v)->tp_name);
220220
return NULL;
221221
}

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14839,7 +14839,7 @@ mainformatlong(PyObject *v,
1483914839
break;
1484014840
default:
1484114841
PyErr_Format(PyExc_TypeError,
14842-
"%%%c format: a number is required, "
14842+
"%%%c format: a real number is required, "
1484314843
"not %.200s",
1484414844
type, Py_TYPE(v)->tp_name);
1484514845
break;

0 commit comments

Comments
 (0)